との基本的な違いを学びまし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
a で sayを呼び出すと、List[Int]
文字通り、整数のリスト全体が単一の値に縮小されます。これは、型Int
(または のスーパータイプInt
、したがって[B >: A]
) になります。
foldLeft
a で sayを呼び出すとList[Int]
、リスト全体 (紙を転がすことを想像してください) が 1 つの値に折りたたまれますが、この値はInt
(したがって[B]
) に関連している必要はありません。
次に例を示します。
def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
(resultingTuple, currentInteger) =>
(currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}
このメソッドは を受け取り、またはList[Int]
を返します。合計を計算し、整数のリストとその合計を含むタプルを返します。ちなみに、代わりにを使用したため、リストは逆向きに返されます。Tuple2[List[Int], Int]
(List[Int], Int)
foldLeft
foldRight
より詳細な説明については、One Fold to rule them all をご覧ください。
reduceLeft
便利な方法です。と同等です
list.tail.foldLeft(list.head)(_)
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 つの値が見つからない場合、有効な値はありません。
参考reduceLeft
までに、空のコンテナに適用すると以下のエラーでエラーになります。
java.lang.UnsupportedOperationException: empty.reduceLeft
使用するコードを作り直す
myList foldLeft(List[String]()) {(a,b) => a+b}
可能性のあるオプションの 1 つです。もう 1 つはreduceLeftOption
、オプションでラップされた結果を返すバリアントを使用することです。
myList reduceLeftOption {(a,b) => a+b} match {
case None => // handle no result as necessary
case Some(v) => println(v)
}
それらが両方とも Scala 標準ライブラリにある基本的な理由は、おそらく両方とも Haskell 標準ライブラリ ( と と呼ばれるfoldl
)にあるためfoldl1
です。そうreduceLeft
でない場合は、さまざまなプロジェクトで便利なメソッドとして定義されることがよくあります。
Scala の関数型プログラミングの原則(Martin Odersky)から:
関数
reduceLeft
は、より一般的な関数 で定義されますfoldLeft
。
foldLeft
は似ていますが、追加のパラメーターとしてaccumulatorreduceLeft
を取り、空のリストで呼び出されたときに返されます。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)(_)