web-dev-qa-db-ja.com

ScalaのfoldLeftとreduceLeftの違い

foldLeftreduceLeftの基本的な違いを学びました

foldLeft:

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

reduceLeft:

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

他に違いはありますか?

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

184
Rajesh Pitty

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

  • あなたの質問はleftとは何の関係もありません。それはむしろ、縮小と折りたたみの違いに関するものです。
  • 違いは実装ではなく、署名を見てください。
  • この質問は、特にScalaとは何の関係もありません。むしろ、関数型プログラミングの2つの概念に関するものです。

質問に戻る:

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の特殊なケースです(ちなみに、sometimesは、どちらかを使用して同じことを表現できることを意味します)。

reduceLeftList[Int]で呼び出すと、整数のリスト全体が文字列で単一の値に縮小され、Int型(またはIntのスーパータイプ、したがって[B >: A])になります。

foldLeftList[Int]で呼び出すと、リスト全体が折り返されて(1枚の紙を転がすことを想像して)単一の値になりますが、この値はIntに関連する必要さえありません(したがって[B])。

以下に例を示します。

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List[Int](), 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

このメソッドはList[Int]を取り、Tuple2[List[Int], Int]または(List[Int] -> Int)を返します。合計を計算し、整数のリストとその合計を含むタプルを返します。ところで、foldLeftの代わりにfoldRightを使用したため、リストは逆方向に返されます。

より詳細な説明については、 すべてを支配するために1つ折り を参照してください。

287
agilesteel

reduceLeftは単なる便利なメソッドです。それは同等です

list.tail.foldLeft(list.head)(_)
184
Kim Stebel

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

43
thoredge

参考のために、空のコンテナにreduceLeftを適用すると、次のエラーが発生してエラーになります。

Java.lang.UnsupportedOperationException: empty.reduceLeft

使用するコードの修正

myList foldLeft(List[String]()) {(a,b) => a+b}

1つの潜在的なオプションです。別の方法は、Optionラップされた結果を返すreduceLeftOptionバリアントを使用することです。

myList reduceLeftOption {(a,b) => a+b} match {
  case None    => // handle no result as necessary
  case Some(v) => println(v)
}
5
Martin Smith

両方がScala標準ライブラリにある基本的な理由は、おそらく両方がHaskell標準ライブラリ(foldlfoldl1と呼ばれる)にあるためです。 reduceLeftがそうでない場合は、別のプロジェクトで便利なメソッドとして定義されることがよくあります。

5
Alexey Romanov

Scalaの関数型プログラミングの原則 (Martin Odersky)から:

関数reduceLeftは、より一般的な関数foldLeftの観点から定義されています。

foldLeftreduceLeftと似ていますが、空のリストでzが呼び出されたときに返される追加パラメーターとして、accumulatorfoldLeftを取ります。

(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)
  }
}

foldLeftU型の値を返すことに注意してください。これは必ずしもList[T]と同じ型である必要はありませんが、reduceLeftはリストと同じ型の値を返します。

2
C8H10N4O2

Fold/reduceで何をしているのかを本当に理解するには、以下を確認してください。 http://wiki.tcl.tk/1798 非常に良い説明。フォールドの概念が得られると、reduceは上記の答えと一緒になります:list.tail.foldLeft(list.head)(_)

0
Henry H.