Уменьшить, сложить или сканировать (слева / справа)?
когда я должен использовать reduceLeft
,reduceRight
,foldLeft
,foldRight
,scanLeft
или scanRight
?
Я хочу интуицию / обзор их различий-возможно, с некоторыми простыми примерами.
2 ответа:
в общем случае все 6-кратные функции применяют двоичный оператор к каждому элементу коллекции. Результат каждого шага передается на следующий шаг (в качестве входных данных для одного из двух аргументов двоичного оператора). Таким образом, мы можем кумулировать результат.
reduceLeft
иreduceRight
набрать единый результат.
foldLeft
иfoldRight
кумулировать один результат, используя начальное значение.
scanLeft
иscanRight
накопить коллекцию промежуточных совокупные результаты с использованием начального значения.аккумулировать
слева и вперед...
С коллекцией элементов
abc
и бинарный операторadd
мы можем исследовать, что делают различные функции сгиба при переходе вперед от левого элемента коллекции (от 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
справа и сзади...если мы начнем с правильного элемента и пойдем назад (от 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) это может быть не то, что вы хотели интуитивно ожидал!
как вы видите, вы можете проверить, что делает ваш foldX, просто запустив scanX вместо этого и отлаживая кумулированный результат на каждом шаге.
нижняя строка
- суммировать результат с
reduceLeft
илиreduceRight
.- суммировать результат с
foldLeft
илиfoldRight
если у вас есть начальное значение.накопить коллекцию промежуточных результатов с
scanLeft
илиscanRight
.используйте вариант xLeft, если вы хотите пойти вперед путем сбора.
- используйте xright вариант, если вы хотите пойти назад путем сбора.
нормально уменьшите, сложите, работы метода развертки путем аккумулировать данные на левой стороне и держите на изменять правую переменную. Основное различие между ними-уменьшить, сложить: -
фолд всегда будет начинаться с
seed
значение, т. е. заданное пользователем начальное значение. Reduce выдаст исключение, если коллекция пуста, где as fold возвращает начальное значение. всегда будет приводить к одному значению.сканирование используется для некоторого порядка обработки элементов слева или справа стороны, то мы можем использовать предыдущий результат в последующих расчетов. Это значит, что мы можем сканировать предметы. всегда будет результатом коллекции.
- метод LEFT_REDUCE работает аналогично методу REDUCE.
RIGHT_REDUCE противоположен reduceLeft one, т. е. он накапливает значения справа и продолжает изменять левую переменную.
reduceLeftOption и reduceRightOption похожи на left_reduce и right_reduce разница только в том, что они возвращают результаты в OPTION object.
часть вывода для приведенного ниже кода будет: -
используя
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")
- {А,B}=>АВ {А. Б,с}=>АВС {АВС,Д}=>АВСD {АБВГД,е}=>ABCDE уменьшите (а+б) стр
- {А,B}=>АВ {А. Б,с}=>АВС {АВС,Д}=>АВСD {АБВГД,е}=>АБВГД reduceLeft (а+б) стр
- {А,B}=>БА {БА,ц}=>ЦБ {ЦБА,Д}=>надо {надо,е}=>reduceLeft EDCBA (Б+а) EDCB
- {D,Е}=>Де {с,де}=>КД {Б,экв}=>право на изменения bcde {а право на изменения bcde}=>АБВГД reduceRight (а+б) 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})) }