Function foldr和foldl进一步解释和示例

Function foldr和foldl进一步解释和示例,function,haskell,syntax,combinators,fold,Function,Haskell,Syntax,Combinators,Fold,我看过和,还有一些其他的,他们解释得相当好 在这种情况下,我仍然不知道lambda是如何工作的 foldr (\y ys -> ys ++ [y]) [] [1,2,3] 有人能一步一步地解释一下吗 以及foldl如何工作?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

我看过和,还有一些其他的,他们解释得相当好

在这种情况下,我仍然不知道lambda是如何工作的

foldr (\y ys -> ys ++ [y]) [] [1,2,3]
有人能一步一步地解释一下吗


以及
foldl
如何工作?

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]
使用

让我们打开包装:

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]

中缀符号在这里可能更清晰

让我们从定义开始:

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]

foldr是一件容易的事情:

foldr :: (a->b->b) -> b -> [a] -> b
它采用了一个类似于(:)的函数

以及类似于空列表[]的值

[] :: [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。想象中缀符号中的f是从右边来的吃豆人

foldl(foldLEFT)从左侧执行相同的操作,但是用中缀符号编写的转换函数从右侧获取其输入参数。因此,机器从左端开始使用列表。Pacman从左到右张开嘴来消耗列表,因为嘴是(b->a->b)而不是(a->b->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)+…)浪费内存,然后对所有公式进行求值


第三个与第二个相似,但与另一个公式相同。

我首先记住这一点的方法是使用关联敏感的减法运算:

foldl (\a b -> a - b) 1 [2] = -1
foldr (\a b -> a - b) 1 [2] = 1
其次,
foldl
从列表的最左边或第一个元素开始,而
foldr
从列表的最右边或最后一个元素开始。这在上面并不明显,因为列表只有一个元素

我的记忆法是这样的:
描述了两件事:

  • 减号(
    -
    )符号的位置
  • 列表的起始元素

+1用于解释语义和进行类比。到目前为止,其他答案只给出了形式上的简化,这一点很重要,但对于在概念层面上的理解,IMHO来说更为重要。因为是
foldr
,我会开始从最右边的列表元素中选取,这意味着
3
。在第一次lambda函数调用时,
3
绑定到
y
,空列表
[]
绑定到
ys
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 (either (\l ~(ls,rs)->(l:ls,rs))
              (\r ~(ls,rs)->(ls,r:rs))
      ) ([],[]) :: [Either l r]->([l],[r])
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
foldl (\a b -> a - b) 1 [2] = -1
foldr (\a b -> a - b) 1 [2] = 1