ときに私が使用する必要がありreduceLeft
、reduceRight
、foldLeft
、foldRight
、scanLeft
またはscanRight
?
私はそれらの違いの直観/概要を知りたい-おそらくいくつかの簡単な例で。
reduce
とfold
開始値の存在ではない-むしろ、つまり結果より深い基礎となる数学的な理由の。
ときに私が使用する必要がありreduceLeft
、reduceRight
、foldLeft
、foldRight
、scanLeft
またはscanRight
?
私はそれらの違いの直観/概要を知りたい-おそらくいくつかの簡単な例で。
reduce
とfold
開始値の存在ではない-むしろ、つまり結果より深い基礎となる数学的な理由の。
回答:
一般に、6つのFold関数はすべて、コレクションの各要素に2項演算子を適用します。各ステップの結果は、次のステップに渡されます(2項演算子の2つの引数の1つへの入力として)。このようにして、結果を累積できます。
reduceLeft
reduceRight
単一の結果を累積します。
foldLeft
そしてfoldRight
、開始値を使用して単一の結果を累積します。
scanLeft
そしてscanRight
、開始値を使用して中間累積結果のコレクションを累積します。
左から転送...
要素のコレクションabc
と2項演算子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)
。
左から転送...
代わりに、私たちがしていた場合は解除CUMULATEをいくつかの結果を減算することによって、コレクションの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番目のパラメーターres
に渡されることに注意してくださいminus
。
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)
最後のリスト(-2、3、-1、4、0)は、あなたが直感的に期待するものではないかもしれません!
ご覧のとおり、代わりにscanXを実行するだけでfoldXの動作を確認し、各ステップで累積結果をデバッグできます。
reduceLeft
またはで結果を累積しreduceRight
ます。foldLeft
かfoldRight
どうかで結果を累積します。scanLeft
またはを使用して、中間結果のコレクションを累積しscanRight
ます。
どこに行きたい場合はxLeft変動を使用し転送し、コレクションを。
List
から適用することから成りますfoldLeft
。他のコレクションは異なる戦略を実装する場合があります。一般的に、場合foldLeft
とfoldRight
(印加オペレータの連想性)交換可能に使用することができ、その後、foldLeft
より効率的で好ましいです。
通常、REDUCE、FOLD、SCANメソッドは、LEFTにデータを蓄積し、RIGHT変数を変更し続けることで機能します。それらの間の主な違いはREDUCE、FOLDは次のとおりです-
フォールドは常にseed
値、つまりユーザー定義の開始値から始まります。foldがシード値を返すときにコレクションが空の場合、Reduceは例外をスローします。常に単一の値になります。
スキャンは、アイテムの左側または右側からのいくつかの処理順序に使用され、その後の計算で以前の結果を利用できます。つまり、アイテムをスキャンできます。常にコレクションになります。
RIGHT_REDUCEはreduceLeft 1の反対です。つまり、RIGHTに値を累積し、左の変数を変更し続けます。
reduceLeftOptionとreduceRightOptionは、left_reduceと同様です。right_reduceは、OPTIONオブジェクトで結果を返す点のみ異なります。
以下のコードの出力の一部は次のようになります:-
scan
数値のリストに対する操作の使用(seed
value を使用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)リスト(0、-2、-3、-3、-2、0)
{0、-2} =>-2 {-2、-1} =>-3 {-3,0} =>-3 {-3,1} =>-2 {-2,2} => 0 scanLeft(b + a)リスト(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)リスト( 0、2、3、3、2、0)
使用してreduce
、fold
文字列のリスト上の操作をList("A","B","C","D","E")
コード:
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}))
}
要素x0、x1、x2、x3および任意の関数fを持つコレクションxの場合、次のようになります。
1. x.reduceLeft (f) is f(f(f(x0,x1),x2),x3) - notice 3 function calls
2. x.reduceRight (f) is f(f(f(x3,x2),x1),x0) - notice 3 function calls
3. x.foldLeft (init,f) is f(f(f(f(init,x0),x1),x2),x3) - notice 4 function calls
4. x.foldRight(init,f) is f(f(f(f(init,x3),x2),x1),x0) - notice 4 function calls
5. x.scanLeft (init,f) is f(init,x0)=g0
f(f(init,x0),x1) = f(g0,x1) = g1
f(f(f(init,x0),x1),x2) = f(g1,x2) = g2
f(f(f(f(init,x0),x1),x2),x3) = f(g2,x3) = g3
- notice 4 function calls but also 4 emitted values
- last element is identical with foldLeft
6. x.scanRight (init,f) is f(init,x3)=h0
f(f(init,x3),x2) = f(h0,x2) = h1
f(f(f(init,x3),x2),x1) = f(h1,x1) = h2
f(f(f(f(init,x3),x2),x1),x0) = f(h2,x0) = h3
- notice 4 function calls but also 4 emitted values
- last element is identical with foldRight
scan
に似てfold
いますが、すべての中間値も出力しますreduce
時々見つけるのが少し難しい初期値は必要ありませんfold
見つけるのが少し難しい初期値が必要です:
x.reduceLeft(f) === x.drop(1).foldLeft(x.head,f)
x.foldRight(init,f) === x.reverse.foldLeft(init,f)
x.foldLeft(init,f) === x.scanLeft(init,f).last