Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell、Foldr和foldl_Haskell_Fold - Fatal编程技术网

Haskell、Foldr和foldl

Haskell、Foldr和foldl,haskell,fold,Haskell,Fold,很长一段时间以来,我一直在想foldr和foldl,我决定下面的问题应该为我解决。假设您将以下列表[1,2,3]传递到以下四个函数中: a = foldl (\xs y -> 10*xs -y) 0 b = foldl (\xs y -> y - 10 * xs) 0 c = foldr (\y xs -> y - 10 * xs) 0 d = foldr (\y xs -> 10 * xs -y) 0 结果将分别为-123、83、281和-321 为什么会这样?我知道

很长一段时间以来,我一直在想foldr和foldl,我决定下面的问题应该为我解决。假设您将以下列表[1,2,3]传递到以下四个函数中:

a = foldl (\xs y -> 10*xs -y) 0
b = foldl (\xs y -> y - 10 * xs) 0
c = foldr (\y xs -> y - 10 * xs) 0
d = foldr (\y xs -> 10 * xs -y) 0
结果将分别为-123、83、281和-321

为什么会这样?我知道当你把[1,2,3,4]传递到一个定义为

f = foldl (xs x -> xs ++ [f x]) []
它被扩展为(([]++[1])++[2])++[3])++[4]

同样,上面的函数a、b、c和d扩展到了什么?

我认为上面的两幅图很好地解释了这一点

由于您的操作不是可交换的,因此
foldr
foldl
的结果将不同,而在可交换操作中,它们将:

Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
使用
scanl
scanr
获取包含中间结果的列表是查看发生了什么的好方法:

Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]
因此,在第一种情况下,我们有((1*1)*2)*3),而在第二种情况下,它是(1*(2*(1*3))。

我认为上面的两个图像很好地解释了这一点

由于您的操作不是可交换的,因此
foldr
foldl
的结果将不同,而在可交换操作中,它们将:

Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
使用
scanl
scanr
获取包含中间结果的列表是查看发生了什么的好方法:

Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]
因此,在第一种情况下,我们有((1*1)*2)*3),而在第二种情况下,它是(1*(2*(1*3))。

我认为上面的两个图像很好地解释了这一点

由于您的操作不是可交换的,因此
foldr
foldl
的结果将不同,而在可交换操作中,它们将:

Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
使用
scanl
scanr
获取包含中间结果的列表是查看发生了什么的好方法:

Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]
因此,在第一种情况下,我们有((1*1)*2)*3),而在第二种情况下,它是(1*(2*(1*3))。

我认为上面的两个图像很好地解释了这一点

由于您的操作不是可交换的,因此
foldr
foldl
的结果将不同,而在可交换操作中,它们将:

Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
使用
scanl
scanr
获取包含中间结果的列表是查看发生了什么的好方法:

Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]

因此,在第一种情况下,我们有(((1*1)*2)*3),而在第二种情况下,它是(1*(2*(1*3))。

可以将
折叠*
函数视为在传递给它的列表上循环,从列表的末尾(
foldr
)或列表的开头(
foldl
)。对于它找到的每个元素,它都将该元素和累加器的当前值传递给您编写的lambda函数。此函数返回的任何值都将在下一次迭代中用作累加器的值

稍微更改您的符号(
acc
而不是
xs
),以显示更清晰的含义,用于第一次左折

a = foldl (\acc y -> 10*acc - y) 0              [1, 2, 3]
  = foldl (\acc y -> 10*acc - y) (0*1 - 1)      [2, 3]
  = foldl (\acc y -> 10*acc - y) -1             [2, 3]
  = foldl (\acc y -> 10*acc - y) (10*(-1) - 2)  [3]
  = foldl (\acc y -> 10*acc - y) (-12)          [3]
  = foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
  = foldl (\acc y -> 10*acc - y) (-123)         []
  = (-123)
对于第一次右折叠(注意累加器在lambda函数的参数中的位置不同)


fold*
函数可以看作是在传递给它的列表上循环,从列表的末尾(
foldr
)或列表的开头(
foldl
)开始。对于它找到的每个元素,它都将该元素和累加器的当前值传递给您编写的lambda函数。此函数返回的任何值都将在下一次迭代中用作累加器的值

稍微更改您的符号(
acc
而不是
xs
),以显示更清晰的含义,用于第一次左折

a = foldl (\acc y -> 10*acc - y) 0              [1, 2, 3]
  = foldl (\acc y -> 10*acc - y) (0*1 - 1)      [2, 3]
  = foldl (\acc y -> 10*acc - y) -1             [2, 3]
  = foldl (\acc y -> 10*acc - y) (10*(-1) - 2)  [3]
  = foldl (\acc y -> 10*acc - y) (-12)          [3]
  = foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
  = foldl (\acc y -> 10*acc - y) (-123)         []
  = (-123)
对于第一次右折叠(注意累加器在lambda函数的参数中的位置不同)


fold*
函数可以看作是在传递给它的列表上循环,从列表的末尾(
foldr
)或列表的开头(
foldl
)开始。对于它找到的每个元素,它都将该元素和累加器的当前值传递给您编写的lambda函数。此函数返回的任何值都将在下一次迭代中用作累加器的值

稍微更改您的符号(
acc
而不是
xs
),以显示更清晰的含义,用于第一次左折

a = foldl (\acc y -> 10*acc - y) 0              [1, 2, 3]
  = foldl (\acc y -> 10*acc - y) (0*1 - 1)      [2, 3]
  = foldl (\acc y -> 10*acc - y) -1             [2, 3]
  = foldl (\acc y -> 10*acc - y) (10*(-1) - 2)  [3]
  = foldl (\acc y -> 10*acc - y) (-12)          [3]
  = foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
  = foldl (\acc y -> 10*acc - y) (-123)         []
  = (-123)
对于第一次右折叠(注意累加器在lambda函数的参数中的位置不同)


fold*
函数可以看作是在传递给它的列表上循环,从列表的末尾(
foldr
)或列表的开头(
foldl
)开始。对于它找到的每个元素,它都将该元素和累加器的当前值传递给您编写的lambda函数。此函数返回的任何值都将在下一次迭代中用作累加器的值

稍微更改您的符号(
acc
而不是
xs
),以显示更清晰的含义,用于第一次左折

a = foldl (\acc y -> 10*acc - y) 0              [1, 2, 3]
  = foldl (\acc y -> 10*acc - y) (0*1 - 1)      [2, 3]
  = foldl (\acc y -> 10*acc - y) -1             [2, 3]
  = foldl (\acc y -> 10*acc - y) (10*(-1) - 2)  [3]
  = foldl (\acc y -> 10*acc - y) (-12)          [3]
  = foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
  = foldl (\acc y -> 10*acc - y) (-123)         []
  = (-123)
对于第一次右折叠(注意累加器在lambda函数的参数中的位置不同)


foldr
是一个非常简单的函数思想:获取一个包含两个参数的函数,获取一个起点、一个列表,并以这种方式计算调用列表中函数的结果

这里有一个很好的小提示,告诉你如何想象在
foldr
调用过程中会发生什么:

foldr (+) 0 [1,2,3,4,5]
=> 1 + (2 + (3 + (4 + (5 + 0))))
我们都知道
[1,2,3,4,5]=1:2:3:4:5:[]
。您需要做的就是用起点替换
[]
,用我们使用的任何函数替换
。当然,我们也可以用同样的方法重建列表:

foldr (:) [] [1,2,3]
=> 1 : (2 : (3 : []))
如果查看签名,我们可以更好地了解函数中发生的情况:

foldr :: (a -> b -> b) -> b -> [a] -> b
我们首先看到函数