私は基本的な違いを学んだの間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]
)。
foldLeft
say 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
、Fruit
sのリスト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)
}
}
foldLeft
typeの値を返すことに注意してくださいU
。これは必ずしもと同じ型ではありませんList[T]
が、reduceLeftはリストと同じ型の値を返します)。
fold / reduceで何をしているのかを本当に理解するには、これをチェックしてください:http : //wiki.tcl.tk/17983 非常に良い説明。フォールドの概念を取得すると、reduceは上記の答えと一緒になります:list.tail.foldLeft(list.head)(_)