Foldr и Foldl дальнейшие объяснения и примеры - PullRequest
11 голосов
/ 16 октября 2010

Я смотрел на различные сгибы и сгибание в целом , а также несколько других, и они объясняют это довольно хорошо.

У меня все еще естьБеда в том, как лямбда будет работать в этом случае.

foldr (\y ys -> ys ++ [y]) [] [1,2,3]

Может ли кто-нибудь пройти этот шаг за шагом и попытаться объяснить это мне?

А также как foldl будет работать?

Ответы [ 4 ]

29 голосов
/ 17 октября 2010

foldr - простая вещь:

foldr :: (a->b->b) -> b -> [a] -> b

Требуется функция, которая чем-то похожа на (:),

(:) :: a -> [a] -> [a]

и значение, похожее на пустой список[],

[] :: [a]

и заменяет каждый: и [] в некотором списке.

Это выглядит так:

foldr f e (1:2:3:[]) = 1 `f` (2 `f` (3 `f` e))

Вы можете представить foldr как некоторое состояние-machine -valuator, тоже:

f - это переход,

f :: input -> state -> state

и e - начальное состояние.

e :: state

foldr (foldRIGHT) запускает состояние-машина с переходом f и начальным состоянием e над списком входов, начиная с правого конца.Представьте f в инфиксной записи как pacman, идущий из -RIGHT.

foldl (foldLEFT) делает то же самое из -LEFT, но функция перехода, написанная в инфиксной записи, получает входной аргумент справа.Таким образом, машина использует список, начиная с левого конца.Пакман использует список из-LEFT с открытым ртом вправо из-за рта (b-> a-> b) вместо (a-> b-> b).

foldl :: (b->a->b) -> b -> [a] -> b

Toпоясните, представьте функцию (-) в качестве перехода:

foldl (-) 100 [1]         = 99 = ((100)-1)
foldl (-) 100 [1,2]       = 97 = (( 99)-2) = (((100)-1)-2)
foldl (-) 100 [1,2,3]     = 94 = (( 97)-3)
foldl (-) 100 [1,2,3,4]   = 90 = (( 94)-4)
foldl (-) 100 [1,2,3,4,5] = 85 = (( 90)-5)

foldr (-) 100 [1]         = -99 = (1-(100))
foldr (-) 100 [2,1]       = 101 = (2-(-99)) = (2-(1-(100)))
foldr (-) 100 [3,2,1]     = -98 = (3-(101))
foldr (-) 100 [4,3,2,1]   = 102 = (4-(-98))
foldr (-) 100 [5,4,3,2,1] = -97 = (5-(102))

Возможно, вы захотите использовать foldr в ситуациях, когда список может быть бесконечным, а оценка должна быть ленивой:

foldr (either (\l ~(ls,rs)->(l:ls,rs))
              (\r ~(ls,rs)->(ls,r:rs))
      ) ([],[]) :: [Either l r]->([l],[r])

И вы, вероятно, захотите использовать строгую версию foldl, которая является foldl ', когда вы используете весь список для вывода.Он может работать лучше и может предотвратить появление переполнения стека или исключений нехватки памяти (в зависимости от компилятора) из-за очень длинных списков в сочетании с отложенной оценкой:

foldl' (+) 0 [1..100000000] = 5000000050000000
foldl  (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci
foldr  (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci

Первый - шагшаг за шагом - создает одну запись в списке, оценивает и использует ее.

Второй создает сначала очень длинную формулу, тратит память на ((... ((0 + 1) +2) +3) + ...), а затем оценивает все это.

Третий похож на второй, но с другой формулой.

13 голосов
/ 17 октября 2010

Используя

foldr f z []     = z
foldr f z (x:xs) = x `f` foldr f z xs

И

k y ys = ys ++ [y]

Распаковываем:

foldr k [] [1,2,3]
= k 1 (foldr k [] [2,3]
= k 1 (k 2 (foldr k [] [3]))
= k 1 (k 2 (k 3 (foldr k [] [])))
= (k 2 (k 3 (foldr k [] []))) ++ [1]
= ((k 3 (foldr k [] [])) ++ [2]) ++ [1]
= (((foldr k [] []) ++ [3]) ++ [2]) ++ [1]
= ((([]) ++ [3]) ++ [2]) ++ [1]
= (([3]) ++ [2]) ++ [1]
= ([3,2]) ++ [1]
= [3,2,1]
5 голосов
/ 17 октября 2010

Определение foldr:

foldr f z []     = z
foldr f z (x:xs) = f x (foldr f z xs)

Итак, пошаговое сокращение вашего примера:

  foldr (\y ys -> ys ++ [y]) [] [1,2,3]
= (\y ys -> ys ++ [y]) 1 (foldr (\y ys -> ys ++ [y]) [] [2,3])
= (foldr (\y ys -> ys ++ [y]) [] [2,3]) ++ [1]
= (\y ys -> ys ++ [y]) 2 (foldr (\y ys -> ys ++ [y]) [] [3]) ++ [1]
= (foldr (\y ys -> ys ++ [y]) [] [3]) ++ [2] ++ [1]
= (\y ys -> ys ++ [y]) 3 (foldr (\y ys -> ys ++ [y]) [] []) ++ [2] ++ [1]
= (foldr (\y ys -> ys ++ [y]) [] []) ++ [3] ++ [2] ++ [1]
= [] ++ [3] ++ [2] ++ [1]
= [3,2,1]
3 голосов
/ 17 октября 2010

Инфиксная запись, вероятно, будет здесь более понятной.

Давайте начнем с определения:

foldr f z []     = z
foldr f z (x:xs) = x `f` (foldr f z xs)

Для краткости напишем g вместо (\y ys -> ys ++ [y]). Следующие строки эквивалентны:

foldr g [] [1,2,3]
1 `g` (foldr g [] [2,3])
1 `g` (2 `g` (foldr g [] [3]))
1 `g` (2 `g` (3 `g` (foldr g [] [])))
1 `g` (2 `g` (3 `g` []))
(2 `g` (3 `g` [])) ++ [1]
(3 `g` []) ++ [2] ++ [1]
[3] ++ [2] ++ [1]
[3,2,1]
...