9

List [Option [Int]]があり、適用可能なファンクターを使用して合計したいと思います。[1]から次のようなものになるはずだと理解しています

import scalaz._
import Scalaz._

List(1,2,3).map(some(_)).foldLeft(some(0))({
    case (acc,value) => (acc <|*|> value){_+_}
})

しかし、私はこれを書く正しい方法を理解することができません。誰かがこれを手伝ってくれたら嬉しいです。

どうもありがとうございます

[1] Scalaでオプション値を組み合わせる方法は?

編集

すべての素晴らしい答えをありがとう。

リストにNoneがある場合は、Noneを返します。Null/ExceptionをOption/Eitherに置き換えて、使用可能なコードを生成できるかどうかを確認しようとしています。

一部の関数がリストを埋めてしまうので、要素の1つがNoneであるかどうかを確認せずに、できるだけ簡単に処理したいと思います。これは、関数でチェックする必要がない例外と同様に機能するはずですが、呼び出し元に処理を任せます。

4

6 に答える 6

15

これにはScalazは本当に必要ありません。リストをフラット化すると、リストがに変換されList[Int]、だったアイテムがすべて削除されNoneます。次に、それを減らすことができます:

List(Some(1), None, Some(2), Some(3), None).flatten.reduce(_ + _) //returns 6: Int
于 2011-11-16T05:38:35.510 に答える
9

あなたが持っていてOption[T]Monoidforがある場合Tは、:がありMonoid[Option[T]]ます

implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] {
  val monoid = implicitly[Monoid[T]]
  val zero = None
  def append(o1: Option[T], o2: =>Option[T]) = (o1, o2) match {
    case (Some(a), Some(b)) => Some(monoid.append(a, b))
    case (Some(a), _)       => o1
    case (_, Some(b))       => o2
    case _                  => zero
  }
}

これを装備したら、次を使用できますsumfoldMap(identity)@missingfaktorによって提案されているように、よりも優れています)。

 List(Some(1), None, Some(2), Some(3), None).asMA.sum === Some(6)

アップデート

実際にApplicativeを使用して、上記のコードを簡略化できます。

implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] {
   val monoid = implicitly[Monoid[T]]
   val zero = None
   def append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _))
}

これにより、さらに一般化して次のようにできると思います。

implicit def applicativeOfMonoidIsMonoid[F[_] : Applicative, T : Monoid]: Monoid[F[T]] = 
  new Monoid[F[T]] {
    val applic = implicitly[Applicative[F]]
    val monoid = implicitly[Monoid[T]]

    val zero = applic.point(monoid.zero)
    def append(o1: F[T], o2: =>F[T]) = (o1 |@| o2)(monoid.append(_, _))
  }

そのように、リストのリスト、ツリーのリスト、...を合計することもできます。

UPDATE2

質問を明確にすると、上記のUPDATEが正しくないことがわかります。

まず、optionTIsMonoidリファクタリングされた、は最初の定義と同等ではありません。最初の定義は値をスキップし、2番目の定義は入力リストにaが含まれるとすぐにNone戻るためです。しかし、その場合、これは!ではありません。確かに、はモノイドの法則を尊重しなければならず、単位元でなければなりません。NoneNoneMonoidMonoid[T]zero

我々が持っている必要があります:

zero    |+| Some(a) = Some(a)
Some(a) |+| zero    = Some(a)

しかし、 forMonoid[Option[T]]を使用するApplicativeための定義を提案したときOption、これは当てはまりませんでした。

None    |+| Some(a) = None
None    |+| None    = None
=> zero |+| a      != a

Some(a) |+| None    = zero
None    |+| None    = zero
=> a    |+| zero   != a

修正は難しくありません。次の定義を変更する必要がありますzero

// the definition is renamed for clarity
implicit def optionTIsFailFastMonoid[T : Monoid]: Monoid[Option[T]] = 
  new Monoid[Option[T]] {
    monoid = implicitly[Monoid[T]]
    val zero = Some(monoid.zero)
    append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _))
  }

この場合、次のようになります(with Tas Int):

Some(0) |+| Some(i) = Some(i)
Some(0) |+| None    = None
=> zero |+| a       = a

Some(i) |+| Some(0) = Some(i)
None    |+| Some(0) = None
=> a    |+| zero    = zero

これは、同一性法則が検証されていることを証明します(結合法則が尊重されていることも検証する必要があります...)。

Monoid[Option[T]]これで、リストを合計するときに必要な動作に応じて、自由に使用できる2つができました。sをスキップNoneするか、「高速で失敗する」かです。

于 2011-11-16T05:43:32.993 に答える
6
scala> List(1, 2, 3).map(some).foldLeft(0 some) {
     |   case (r, c) => (r |@| c)(_ + _)
     | }
res180: Option[Int] = Some(6)
于 2011-11-16T05:39:40.477 に答える
5

1つのオプションは、最初にリスト全体を順番に並べてから、通常のように折りたたむことです。

val a: List[Option[Int]] = List(1, 2, 3) map (Some(_))
a.sequence map (_.foldLeft(0)(_+_))
于 2011-11-16T05:33:20.863 に答える
0

ScalazのApplicativeBuilderを使用すると、別のオプションになります。

import scalaz._
import Scalaz._

List(1,2,3).map(_.some).foldl1((acc,v) => (acc |@| v) {_+_}) join
于 2011-11-26T13:51:40.740 に答える
0

しばらく前にこれを見つけました、もうソースを見つけることができません、しかしそれは私のために働いています

 def sumOpt1(lst: List[Option[Int]]): Option[Int] = {
    lst.foldLeft(Option.empty[Int]) {
      case (prev, elem) =>
        (prev, elem) match {
          case (None, None) => None
          case (None, Some(el)) => Some(el)
          case (Some(p), None) => Some(p)
          case (Some(p), Some(el)) => Some(p + el)
        }
    }
  }

また

 def sumOpt2(lst: List[Option[Int]]): Option[Int] = {
    lst.foldLeft(Option.empty[Int]) {
      case (prev, elem) =>
        (prev, elem) match {
          case (None, None) => None
          case (p, el) => Some(p.getOrElse(0) + el.getOrElse(0))
        }
    }
  }

また

def sumOpt3(lst: List[Option[Int]]): Option[Int] = {
    lst.foldLeft(Option.empty[Int]) {
      case (prev, elem) =>
        (prev, elem) match {
          case (None, el) => el
          case (p, None) => p
          case (Some(p), Some(el)) => Some(p + el)
        }
    }
  }
于 2016-09-07T10:56:07.353 に答える