ScalaのfoldLeftとreduceLeftの違い


196

私は基本的な違いを学んだの間foldLeftreduceLeft

foldLeft:

  • 初期値を渡す必要があります

reduceLeft:

  • コレクションの最初の要素を初期値として受け取ります
  • コレクションが空の場合は例外をスローします

他に違いはありますか?

同様の機能を持つ2つのメソッドがある特定の理由は?



「Scalaでのフォールドとリデュースの違い」になるように質問を編集した場合、すばらしいでしょう。
pedram bashiri

回答:


302

実際の答えを出す前に、ここで言及することはほとんどありません。

  • あなたの質問はとは何の関係もありませんがleft、縮小と折りたたみの違いについてです
  • 違いはまったく実装ではなく、署名を確認するだけです。
  • 問題は特にScalaとは関係ありません。関数型プログラミングの2つの概念についてです。

あなたの質問に戻ります:

ここに署名がありますfoldLeftfoldRight私がこれから作成することもできます):

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 <: FoodFruitsのリストBananaがある場合、s が含まれている可能性があるように見えますが、s が含まれている場合Food、タイプはFoodですよね?したがって、この場合、BがのスーパータイプでAあり、BAの両方を含むリストがある場合、リストはではBなくタイプである必要がありAます。この矛盾を説明できますか?
socom1880

私はあなたの質問を正しく理解しているかどうかわかりません。私の5歳の回答が還元関数について言っていることは、a List[Banana]を単一Bananaまたは単一Fruitまたは単一に還元できるということFoodです。なぜならFruit :> Banana、 `Food:> Banana 'だからです。
agilesteel

はい...それは実際に理にかなっていることに感謝します。私はもともとそれを「型のリストにBanana含まれるかもしれない」と解釈してFruitいましたが、これは意味がありません。あなたの説明は理にかなっています- f渡される関数reduce()はa Fruitまたはをもたらす可能性がFoodあります。つまりB、シグネチャではサブクラスではなくスーパークラスであるべきです。
socom1880

193

reduceLeft便利な方法です。に相当

list.tail.foldLeft(list.head)(_)

11
良い、簡潔な答え:) reducelftしかし、綴りを修正したいと思うかもしれません
Hanxue

10
いい答えだ。これは、なぜfold空のリストでreduce機能するのに機能しないのかを示しています。
Mansoor Siddiqui、2015年

44

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つの値が見つからない場合、正当な値はありません。


5

それらが両方ともScala標準ライブラリーにある基本的な理由は、おそらく両方がHaskell標準ライブラリー( foldlおよびとfoldl1)にあるためです。そうreduceLeftでない場合は、さまざまなプロジェクトで便利な方法として定義されることがよくあります。


5

参考までに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)
}

2

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はリストと同じ型の値を返します)。


0

fold / reduceで何をしているのかを本当に理解するには、これをチェックしてください:http : //wiki.tcl.tk/17983 非常に良い説明。フォールドの概念を取得すると、reduceは上記の答えと一緒になります:list.tail.foldLeft(list.head)(_)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.