언제 사용해야한다 reduceLeft
, reduceRight
, foldLeft
, foldRight
, scanLeft
또는 scanRight
?
차이점에 대한 직관 / 개요를 원합니다-간단한 예가 있습니다.
reduce
와 fold
시작 값의 존재가 아니다 - 오히려 그이있다 결과 더 깊은 기초 수학 이유.
언제 사용해야한다 reduceLeft
, reduceRight
, foldLeft
, foldRight
, scanLeft
또는 scanRight
?
차이점에 대한 직관 / 개요를 원합니다-간단한 예가 있습니다.
reduce
와 fold
시작 값의 존재가 아니다 - 오히려 그이있다 결과 더 깊은 기초 수학 이유.
답변:
일반적으로 6 개의 모든 접기 기능은 이진 연산자를 컬렉션의 각 요소에 적용합니다. 각 단계의 결과는 다음 단계로 전달됩니다 (이진 연산자의 두 인수 중 하나에 대한 입력으로). 이런 식 으로 결과를 누적 할 수 있습니다.
reduceLeft
그리고 reduceRight
하나의 결과를 쌓아.
foldLeft
및 foldRight
시작 값을 사용하는 하나의 결과를 쌓아.
scanLeft
및 scanRight
시작 값을 사용하여 중간 누적 결과 집합을 쌓아.
왼쪽부터 앞으로 ...
요소 컬렉션 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로) 되돌아 가면 이제 이항 연산자에 대한 두 번째 인수가 결과를 누적 함을 알 수 있습니다 (연산자가 동일 함) 인수 이름을 전환하여 역할을 명확하게합니다. ) :
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)
.
왼쪽부터 앞으로 ...
대신 경우 우리는했다 드 쌓아 , 우리는 첫 번째 인수를 통해 결과를 쌓아 올린 것 모음의 왼쪽 요소부터 감산에 의해 어떤 결과를 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 변형의 (계산되지 않은) 값은 이항 연산자 의 두 번째 매개 변수 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 변화를 사용하여 전달 컬렉션을.
일반적으로 REDUCE, FOLD, SCAN 방법은 LEFT에 데이터를 누적하고 RIGHT 변수를 계속 변경하여 작동합니다. 그들 사이의 주요 차이점은 REDUCE입니다.
접기는 항상 seed
사용자 정의 시작 값 과 같은 값으로 시작합니다. 접기가 시드 값을 반환하는 컬렉션이 비어 있으면 Reduce에서 예외가 발생합니다. 항상 단일 값이됩니다.
스캔은 왼쪽 또는 오른쪽 항목의 일부 처리 순서에 사용되며 이후 계산에서 이전 결과를 사용할 수 있습니다. 즉, 항목을 스캔 할 수 있습니다. 항상 컬렉션을 만듭니다.
RIGHT_REDUCE는 reduceLeft와 반대입니다. 즉, RIGHT에 값을 누적하고 왼쪽 변수를 계속 변경합니다.
reduceLeftOption 및 reduceRightOption은 left_reduce와 비슷하며 right_reduce는 OPTION 객체에서 결과를 반환한다는 점만 다릅니다.
아래에 언급 된 코드의 출력 부분은 다음과 같습니다.
사용 scan
번호 목록에서 동작을 (사용 seed
가격 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) 목록 ( 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