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) + ...), а затем оценивает все это.
Третий похож на второй, но с другой формулой.