Разница между складками и foldLeft или foldRight?
ПРИМЕЧАНИЕ. Я нахожусь в Scala 2.8-can, что может быть проблемой?
Почему я не могу использовать функцию fold
так же, как foldLeft
или foldRight
?
В Set scaladoc говорится, что:
Результат сгибания может быть только супертипом этого параметра типа параллельной коллекции T
.
Но я не вижу параметра типа T
в сигнатуре функции:
def fold [A1 >: A] (z: A1)(op: (A1, A1) ⇒ A1): A1
В чем разница между foldLeft-Right
и fold
, и как использовать последнюю?
EDIT: Например, как бы я написал сводку, чтобы добавить все элементы в список? С foldLeft
это будет:
val foo = List(1, 2, 3)
foo.foldLeft(0)(_ + _)
// now try fold:
foo.fold(0)(_ + _)
>:7: error: value fold is not a member of List[Int]
foo.fold(0)(_ + _)
^
Ответы
Ответ 1
Вы правы, когда старая версия Scala является проблемой. Если вы посмотрите страницу scaladoc для Scala 2.8.1, вы увидите, что там не определено фолд (что согласуется с вашим сообщением об ошибке). По-видимому, fold
было введено в Scala 2.9.
Ответ 2
Короткий ответ:
foldRight
связывается вправо. То есть элементы будут накапливаться в порядке справа налево:
List(a,b,c).foldRight(z)(f) = f(a, f(b, f(c, z)))
foldLeft
связывается влево. То есть аккумулятор будет инициализирован, и элементы будут добавлены к аккумулятору в порядке слева направо:
List(a,b,c).foldLeft(z)(f) = f(f(f(z, a), b), c)
fold
ассоциативен тем, что порядок, в котором элементы складываются вместе, не определен. То есть аргументы fold
образуют monoid.
Ответ 3
fold
, в отличие от foldRight
и foldLeft
, не дает никаких гарантий относительно порядка, в котором будут обрабатываться элементы коллекции. Вероятно, вы захотите использовать fold
с его более ограниченной подписью с параллельными коллекциями, где отсутствие гарантированного порядка обработки помогает параллельной коллекции реализовать сворачивание параллельно. Причина изменения подписи схожа: с дополнительными ограничениями проще сделать параллельную складку.
Ответ 4
В вашем конкретном примере вы будете кодировать его так же, как и с foldLeft.
val ns = List(1, 2, 3, 4)
val s0 = ns.foldLeft (0) (_+_) //10
val s1 = ns.fold (0) (_+_) //10
assert(s0 == s1)
Ответ 5
Согласитесь с другими ответами. подумал о том, чтобы дать простой иллюстративный пример:
object MyClass {
def main(args: Array[String]) {
val numbers = List(5, 4, 8, 6, 2)
val a = numbers.fold(0) { (z, i) =>
{
println("fold val1 " + z +" val2 " + i)
z + i
}
}
println(a)
val b = numbers.foldLeft(0) { (z, i) =>
println("foldleft val1 " + z +" val2 " + i)
z + i
}
println(b)
val c = numbers.foldRight(0) { (z, i) =>
println("fold right val1 " + z +" val2 " + i)
z + i
}
println(c)
}
}
Результат сам по себе поясняет:
fold val1 0 val2 5
fold val1 5 val2 4
fold val1 9 val2 8
fold val1 17 val2 6
fold val1 23 val2 2
25
foldleft val1 0 val2 5
foldleft val1 5 val2 4
foldleft val1 9 val2 8
foldleft val1 17 val2 6
foldleft val1 23 val2 2
25
fold right val1 2 val2 0
fold right val1 6 val2 2
fold right val1 8 val2 8
fold right val1 4 val2 16
fold right val1 5 val2 20
25
Ответ 6
fold() выполняет параллельную обработку, поэтому не гарантирует порядок обработки.
где как foldLeft и foldRight обрабатывают элементы последовательно слева направо (в случае foldLeft) или справа налево (в случае foldRight)
Примеры суммирования списка -
val numList = List(1, 2, 3, 4, 5)
val r1 = numList.par.fold(0)((acc, value) => {
println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
acc + value
})
println("fold(): " + r1)
println("#######################")
/*
* You can see from the output that,
* fold process the elements of parallel collection in parallel
* So it is parallel not linear operation.
*
* adding accumulator=0, value=4 => 4
* adding accumulator=0, value=3 => 3
* adding accumulator=0, value=1 => 1
* adding accumulator=0, value=5 => 5
* adding accumulator=4, value=5 => 9
* adding accumulator=0, value=2 => 2
* adding accumulator=3, value=9 => 12
* adding accumulator=1, value=2 => 3
* adding accumulator=3, value=12 => 15
* fold(): 15
*/
val r2 = numList.par.foldLeft(0)((acc, value) => {
println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
acc + value
})
println("foldLeft(): " + r2)
println("#######################")
/*
* You can see that foldLeft
* picks elements from left to right.
* It means foldLeft does sequence operation
*
* adding accumulator=0, value=1 => 1
* adding accumulator=1, value=2 => 3
* adding accumulator=3, value=3 => 6
* adding accumulator=6, value=4 => 10
* adding accumulator=10, value=5 => 15
* foldLeft(): 15
* #######################
*/
// --> Note in foldRight second arguments is accumulated one.
val r3 = numList.par.foldRight(0)((value, acc) => {
println("adding value=" + value + ", acc=" + acc + " => " + (value + acc))
acc + value
})
println("foldRight(): " + r3)
println("#######################")
/*
* You can see that foldRight
* picks elements from right to left.
* It means foldRight does sequence operation.
*
* adding value=5, acc=0 => 5
* adding value=4, acc=5 => 9
* adding value=3, acc=9 => 12
* adding value=2, acc=12 => 14
* adding value=1, acc=14 => 15
* foldRight(): 15
* #######################
*/