我查看了different folds和folding in general以及其他一些内容,他们对此进行了相当详细的解释。
在这种情况下,我仍然无法解决lambda的工作原理。
foldr (\y ys -> ys ++ [y]) [] [1,2,3]
有人可以一步一步地尝试向我解释一下吗?
还有foldl
如何运作?
答案 0 :(得分:29)
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想象成一些状态机评估器:
f是过渡,
f :: input -> state -> state
和e是开始状态。
e :: state
foldr(foldRIGHT)从右端开始,在输入列表上运行具有转换f和开始状态e的状态机。想象一下,如果是来自右边的pacman,则使用中缀表示法。
foldl(foldLEFT)从-LEFT执行相同的操作,但是用中缀表示法编写的转换函数从右边获取其输入参数。因此机器从左端开始使用列表。由于口(b-> a-> b)而不是(a-> b-> b),Pacman从右侧开口消耗列表 - 左侧,
foldl :: (b->a->b) -> b -> [a] -> b
为清楚起见,假设函数减去转换:
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)+ ...)浪费记忆,然后评估所有的公式。
第三个就像第二个,但是使用另一个公式。
答案 1 :(得分:13)
使用
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]
答案 2 :(得分:5)
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 :(得分:3)
中缀符号可能会更加清晰。
让我们从定义开始:
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]
答案 4 :(得分:0)
我首先记住这一点的方法是使用关联敏感 减法操作:
foldl (\a b -> a - b) 1 [2] = -1
foldr (\a b -> a - b) 1 [2] = 1
其次,foldl
从列表的最左边或第一个元素开始,而 foldr
从列表的最右边或最后一个元素开始。上面不明显,因为列表只有一个元素。
我的助记符是这样的:left
或 right
描述了两件事:
-
) 的位置