197

reduceLeftreduceRightfoldLeftfoldRightscanLeftまたはをいつ使用する必要がありscanRightますか?

それらの違いの直感/概要が必要です-おそらくいくつかの簡単な例があります。

4

3 に答える 3

387

一般に、6 つのフォールド関数はすべて、コレクションの各要素に二項演算子を適用します。各ステップの結果は、次のステップに渡されます (二項演算子の 2 つの引数の 1 つへの入力として)。このようにして、結果を累積できます。

reduceLeftreduceRight単一の結果を累積します。

foldLeftfoldRight開始値を使用して単一の結果を累積します。

scanLeftscanRight開始値を使用して中間累積結果のコレクションを累積します。

たまる

左から前へ...

要素のコレクションabcと二項演算子addを使用して、コレクションの LEFT 要素から (A から C に) 進むときに、さまざまな折り畳み関数が何をするかを調べることができます。

val abc = List("A", "B", "C")

def add(res: String, x: String) = { 
  println(s"op: $res + $x = ${res + x}")
  res + x
}

abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC    // accumulates value AB in *first* operator arg `res`
// res: String = ABC

abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA      // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC

abc.scanLeft("z")(add)
// op: z + A = zA      // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results


右から後ろへ…

RIGHT 要素から始めて逆方向 (C から A) に進むと、2 項演算子の2 番目の引数が結果を累積していることに気付くでしょう (演算子は同じで、役割を明確にするために引数名を変更しただけです)。 ):

def add(x: String, res: String) = {
  println(s"op: $x + $res = ${x + res}")
  x + res
}

abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC  // accumulates value BC in *second* operator arg `res`
// res: String = ABC

abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz

abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)

.

除算

左から前へ...

代わりに、コレクションの LEFT 要素から開始する減算によって一部の結果を逆累積resする場合は、二項演算子の最初の引数を介して結果を累積しminusます。

val xs = List(1, 2, 3, 4)

def minus(res: Int, x: Int) = {
  println(s"op: $res - $x = ${res - x}")
  res - x
}

xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4  // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8

xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10

xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)


右から後ろへ…

ただし、xRight のバリエーションに注目してください。xRight バリエーションの (逆) 累積値は、 2項演算子の2 番目のパラメーターに渡されることに注意してください。resminus

def minus(x: Int, res: Int) = {
  println(s"op: $x - $res = ${x - res}")
  x - res
}

xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3  // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2

xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2

xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0) 

最後の List(-2, 3, -1, 4, 0) は、直感的に期待するものではないかもしれません!

ご覧のとおり、代わりに scanX を実行するだけで foldX が何をしているかを確認し、各ステップで累積された結果をデバッグできます。

結論

  • reduceLeftまたはで結果を累積しreduceRightます。
  • 開始値がある場合は、foldLeftまたはで結果を累積します。foldRight
  • scanLeftまたはを使用して、中間結果のコレクションを累積しscanRightます。

  • コレクションを順方向に進めたい場合は、xLeft バリエーションを使用します

  • コレクションをさかのぼりたい場合は、xRight バリエーションを使用します
于 2013-07-01T16:04:30.153 に答える
10

通常、REDUCE、FOLD、SCAN メソッドは、LEFT にデータを蓄積し、RIGHT 変数を変更し続けることによって機能します。それらの主な違いは、REDUCE、FOLD です。

折り畳みは常に値、seedつまりユーザー定義の開始値で始まります。コレクションが空の場合、reduce は例外をスローしますが、fold はシード値を返します。常に単一の値になります。

スキャンは、左側または右側からのアイテムの処理順序に使用され、その後の計算で前の結果を利用できます。つまり、アイテムをスキャンできます。常にコレクションになります。

  • LEFT_REDUCE メソッドは、REDUCE メソッドと同様に機能します。
  • RIGHT_REDUCE は reduceLeft の反対です。つまり、RIGHT に値を蓄積し、左の変数を変更し続けます。

  • reduceLeftOption と reduceRightOption は left_reduce と right_reduce に似ていますが、OPTION オブジェクトで結果を返す点だけが異なります。

以下のコードの出力の一部は次のようになります:-

scan数値のリストに対する操作の使用 ( valueseedを使用0)List(-2,-1,0,1,2)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0スキャンリスト(0, -2, -3, -3, -2, 0)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0 scanLeft (a+b) List(0, -2, -3, -3, -2, 0)

  • {0,-2}=>-2 {-2,-1}=>-3 {-3,0}=>-3 {-3,1}=>-2 {-2,2}=>0 scanLeft (b+a) List(0, -2, -3, -3, -2, 0)

  • {2,0}=>2 {1,2}=>3 {0,3}=>3 {-1,3}=>2 {-2,2}=>0 scanRight (a+b) List( 0, 2, 3, 3, 2, 0)

  • {2,0}=>2 {1,2}=>3 {0,3}=>3 {-1,3}=>2 {-2,2}=>0 scanRight (b+a) List( 0, 2, 3, 3, 2, 0)

を使用してreducefold文字列のリストに対する操作List("A","B","C","D","E")

  • {A,B}=>AB {AB,C}=>ABC {ABC,D}=>ABCD {ABCD,E}=>ABCDE reduce (a+b) ABCDE
  • {A,B}=>AB {AB,C}=>ABC {ABC,D}=>ABCD {ABCD,E}=>ABCDE reduceLeft (a+b) ABCDE
  • {A,B}=>BA {BA,C}=>CBA {CBA,D}=>DCBA {DCBA,E}=>EDCBA reduceLeft (b+a) EDCB
  • {D,E}=>DE {C,DE}=>CDE {B,CDE}=>BCDE {A,BCDE}=>ABCDE reduceRight (a+b) ABCDE
  • {D,E}=>ED {C,ED}=>EDC {B,EDC}=>EDCB {A,EDCB}=>EDCBA reduceRight (b+a) EDCBA

コード :

object ScanFoldReduce extends App {

    val list = List("A","B","C","D","E")
            println("reduce (a+b) "+list.reduce((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (a+b) "+list.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (b+a) "+list.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("reduceRight (a+b) "+list.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("reduceRight (b+a) "+list.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  ")
                b+a
            }))

            println("scan            "+list.scan("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanLeft (a+b)  "+list.scanLeft("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanLeft (b+a)  "+list.scanLeft("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))
            println("scanRight (a+b) "+list.scanRight("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanRight (b+a) "+list.scanRight("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))
//Using numbers
     val list1 = List(-2,-1,0,1,2)

            println("reduce (a+b) "+list1.reduce((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (a+b) "+list1.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (b+a) "+list1.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("      reduceRight (a+b) "+list1.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("      reduceRight (b+a) "+list1.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  ")
                b+a
            }))

            println("scan            "+list1.scan(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("scanLeft (a+b)  "+list1.scanLeft(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("scanLeft (b+a)  "+list1.scanLeft(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("scanRight (a+b)         "+list1.scanRight(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b}))

            println("scanRight (b+a)         "+list1.scanRight(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                b+a}))
}
于 2015-06-19T05:28:32.130 に答える