【发布时间】:2021-12-09 15:14:38
【问题描述】:
这里有一个例子来说明我的意思
Prelude> foldr (-) 0 [0,1]
-1
我以为这里基本上可以做到0 - 0 = 0; 1 - 0 = 1;,但实际结果是-1
我用其他例子试过了
Prelude> foldr (-) 1 [1,3]
-1
Prelude> foldr (-) 1 [1,9]
-7
我可能误解了 foldr 的工作原理,所以我很乐意解释一下 :)
【问题讨论】:
这里有一个例子来说明我的意思
Prelude> foldr (-) 0 [0,1]
-1
我以为这里基本上可以做到0 - 0 = 0; 1 - 0 = 1;,但实际结果是-1
我用其他例子试过了
Prelude> foldr (-) 1 [1,3]
-1
Prelude> foldr (-) 1 [1,9]
-7
我可能误解了 foldr 的工作原理,所以我很乐意解释一下 :)
【问题讨论】:
考虑以下函数:
printfoldr f init xs = foldr (\el acc -> "(" ++ el ++ f ++ acc ++ ")") (show init) $ map show xs
此函数模拟foldr 函数如何扩展并输出其字符串表示形式。
让我们进行一些测试:
λ> printfoldr "-" 0 [1,2,3,4]
"(1-(2-(3-(4-0))))"
-- the test cases you provided
λ> printfoldr "-" 0 [1,2]
"(1-(2-0))" -- This reduces to -1 which is the result you had
λ> printfoldr "-" 1 [1,3]
"(1-(3-1))" -- This reduces to -1 which is also the result you had
λ> printfoldr "-" 1 [1,9]
"(1-(9-1))" -- reduces -7 which is also correct
所以一般来说,foldr 类型为 foldr :: (a -> b -> b) -> b -> t a -> b 的工作方式如下:
x0 `f` (x1 `f` ...(xn-2 `f` (xn-1 `f` (xn `f` init))))
其中f 的类型为(a -> b -> b),x 的类型为a,init 的类型为b。
【讨论】:
试试foldr (-) 0 [1, 2, 3, 4, 5]。你应该得到3。那是因为折叠相当于(1 - (2 - (3 - (4 - (5 - 0)))))——它是从右边开始的。如果你尝试foldl,你应该得到-15。那是因为它从左边开始,相当于(((((0 - 1) - 2) - 3) - 4) - 5)。
您的简短示例foldr (-) 0 [0, 1] 等价于0 - (1 - 0),后者简化为-1。
【讨论】:
foldr定义如下:
foldr f b [] = b
foldr f b (x:xs) = f x (foldr f b xs)
您递归折叠列表的尾部,然后将f 应用于头部和递归结果。请注意,基值用于序列的结尾,而不是开头。
仅当f 具有关联性时,将其视为简单地将函数应用于一个又一个值。例如,1 + (2 + (3 + 0)) == ((1+2) + 3) + 0。
但是,减法不是关联的。一般情况下,x - y - z 只等于(x - y) - z,而不是x - (y - z)。
【讨论】: