私は基本的な違いを学んだの間foldLeftとreduceLeft
foldLeft:
- 初期値を渡す必要があります
reduceLeft:
- コレクションの最初の要素を初期値として受け取ります
- コレクションが空の場合は例外をスローします
他に違いはありますか?
同様の機能を持つ2つのメソッドがある特定の理由は?
私は基本的な違いを学んだの間foldLeftとreduceLeft
foldLeft:
reduceLeft:
他に違いはありますか?
同様の機能を持つ2つのメソッドがある特定の理由は?
回答:
実際の答えを出す前に、ここで言及することはほとんどありません。
left、縮小と折りたたみの違いについてですあなたの質問に戻ります:
ここに署名がありますfoldLeft(foldRight私がこれから作成することもできます):
def foldLeft [B] (z: B)(f: (B, A) => B): B
そして、ここに署名がありますreduceLeft(ここでも方向は関係ありません)
def reduceLeft [B >: A] (f: (B, A) => B): B
これら2つは非常によく似ているため、混乱を引き起こしました。reduceLeftの特殊なケースですfoldLeft(ちなみに、どちらかを使用して同じことを表現できることもあります)。
あなたが呼び出すときreduceLeftに言ってList[Int]、それは文字通りのタイプであることを行っている単一の値、に整数のリスト全体を削減しますInt(またはスーパータイプのIntため、[B >: A])。
foldLeftsay on a を呼び出すと、List[Int]リスト全体(紙を丸めることを想定)が単一の値に折りたたまれますが、この値はInt(したがって[B])に関連している必要はありません。
次に例を示します。
def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
(resultingTuple, currentInteger) =>
(currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}
このメソッドはa List[Int]を受け取り、a Tuple2[List[Int], Int]またはを返します(List[Int], Int)。合計を計算し、整数のリストと合計でタプルを返します。ちなみに、のfoldLeft代わりに使用したため、リストは逆方向に返されますfoldRight。
ウォッチ一つは、それらすべてを支配するために折るより深さの説明のために。
BスーパータイプなのAか説明できますか?ように思えるB実際のサブタイプであるべきAではなく、スーパータイプ。たとえばBanana <: Fruit <: Food、FruitsのリストBananaがある場合、s が含まれている可能性があるように見えますが、s が含まれている場合Food、タイプはFoodですよね?したがって、この場合、BがのスーパータイプでAあり、BとAの両方を含むリストがある場合、リストはではBなくタイプである必要がありAます。この矛盾を説明できますか?
List[Banana]を単一Bananaまたは単一Fruitまたは単一に還元できるということFoodです。なぜならFruit :> Banana、 `Food:> Banana 'だからです。
Banana含まれるかもしれない」と解釈してFruitいましたが、これは意味がありません。あなたの説明は理にかなっています- f渡される関数reduce()はa Fruitまたはをもたらす可能性がFoodあります。つまりB、シグネチャではサブクラスではなくスーパークラスであるべきです。
reduceLeft便利な方法です。に相当
list.tail.foldLeft(list.head)(_)
reducelftしかし、綴りを修正したいと思うかもしれません
fold空のリストでreduce機能するのに機能しないのかを示しています。
foldLeftより一般的です。これを使用して、最初に入力したものとは完全に異なるものを生成できます。一方reduceLeft、コレクション型の同じ型またはスーパー型の最終結果しか生成できません。例えば:
List(1,3,5).foldLeft(0) { _ + _ }
List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }
foldLeft最後に折り畳まれた結果に閉鎖(初回の初期値を使用して)、次の値を適用します。
reduceLeft一方、最初にリストから2つの値を組み合わせ、それらをクロージャーに適用します。次に、残りの値を累積結果と組み合わせます。見る:
List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }
リストが空の場合はfoldLeft、正当な結果として初期値を提示できます。reduceLeft一方、リスト内に少なくとも1つの値が見つからない場合、正当な値はありません。
それらが両方ともScala標準ライブラリーにある基本的な理由は、おそらく両方がHaskell標準ライブラリー( foldlおよびとfoldl1)にあるためです。そうreduceLeftでない場合は、さまざまなプロジェクトで便利な方法として定義されることがよくあります。
参考までにreduceLeft、次のエラーで空のコンテナに適用するとエラーになります。
java.lang.UnsupportedOperationException: empty.reduceLeft
使用するコードを作り直す
myList foldLeft(List[String]()) {(a,b) => a+b}
可能なオプションの1つです。もう1つはreduceLeftOption、Optionでラップされた結果を返すバリアントを使用することです。
myList reduceLeftOption {(a,b) => a+b} match {
case None => // handle no result as necessary
case Some(v) => println(v)
}
Scalaでは関数型プログラミングの原則(マーティン・オーダーズキー):
関数
reduceLeftは、より一般的な関数で定義されfoldLeftます。
foldLeftに似てreduceLeftいますが、追加のパラメータとしてアキュムレータを 受け取りzます。これはfoldLeft、空のリストで呼び出されたときに返されます。
(List (x1, ..., xn) foldLeft z)(op) = (...(z op x1) op ...) op x
[とは対照的にreduceLeft、空のリストで呼び出されたときに例外をスローします。]
コース(講義5.5を参照)は、これらの関数の抽象的な定義を提供し、それらの違いを示しますが、パターンマッチングと再帰の使用は非常に似ています。
abstract class List[T] { ...
def reduceLeft(op: (T,T)=>T) : T = this match{
case Nil => throw new Error("Nil.reduceLeft")
case x :: xs => (xs foldLeft x)(op)
}
def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
case Nil => z
case x :: xs => (xs foldLeft op(z, x))(op)
}
}
foldLefttypeの値を返すことに注意してくださいU。これは必ずしもと同じ型ではありませんList[T]が、reduceLeftはリストと同じ型の値を返します)。
fold / reduceで何をしているのかを本当に理解するには、これをチェックしてください:http : //wiki.tcl.tk/17983 非常に良い説明。フォールドの概念を取得すると、reduceは上記の答えと一緒になります:list.tail.foldLeft(list.head)(_)