我定义了两个函数,它们充当反向抛光表示法评估器(后缀评估器):
step :: [Int] -> [Char] -> [Int]
step stack str
| str == "*" = (x*y):remaining
| str == "+" = (x+y):remaining
| str == "-" = (x-y):remaining
| str == "/" = (x `div` y):remaining
| otherwise = (read str :: Int):stack
where x = head $ tail stack
y = head stack
remaining = tail $ tail stack
rpn :: String -> Int
rpn input = head result
where arr = words input
result = foldl step [] arr
因此rpn
将字符串"4 10 -"
评估为4-10 = -6
如果我想创建一个前缀符号评估器("- 4 10"
-> 4-10 = -6
),很明显,我可以简单地重写我的rpn
函数(作为新函数pn
)反转前缀表示法,然后使用foldl逐步完成操作:
pn :: String -> Int
pn input = head result
where arr = reverse $ words input --reverse the input string
result = foldl step [] arr
因此,输入"- 4 10"
变为"10 4 -"
,这是一个问题,因为(-)
运算符在阶跃函数中的不可交换性会导致错误的结果。
现在,我可以简单地通过用较小的(-)
模式重写步函数来以相反的顺序减去变量(x - y
-> y - x
)来解决此问题,但是这种解决方案感觉很冗长,不太符合功能范式的精神。
对我来说很明显,我可以使用完全相同的步进函数简单地在前缀表示法上foldr
而不是foldl
,但是每次尝试执行以下代码时,都无需做任何更改;我收到一个编译错误:
*Main> foldr step [] ["-", "4", "10"]
<interactive>:105:7: error:
• Couldn't match type ‘Int’ with ‘Char’
Expected type: [Int] -> [Int] -> [Int]
Actual type: [Int] -> [Char] -> [Int]
• In the first argument of ‘foldr’, namely ‘step’
In the expression: foldr step [] ["-", "4", "10"]
In an equation for ‘it’: it = foldr step [] ["-", "4", "10"]
<interactive>:105:16: error:
• Couldn't match type ‘Char’ with ‘Int’
Expected type: [Int]
Actual type: [Char]
• In the expression: "-"
In the third argument of ‘foldr’, namely ‘["-", "4", "10"]’
In the expression: foldr step [] ["-", "4", "10"]
<interactive>:105:21: error:
• Couldn't match type ‘Char’ with ‘Int’
Expected type: [Int]
Actual type: [Char]
• In the expression: "4"
In the third argument of ‘foldr’, namely ‘["-", "4", "10"]’
In the expression: foldr step [] ["-", "4", "10"]
<interactive>:105:26: error:
• Couldn't match type ‘Char’ with ‘Int’
Expected type: [Int]
Actual type: [Char]
• In the expression: "10"
In the third argument of ‘foldr’, namely ‘["-", "4", "10"]’
In the expression: foldr step [] ["-", "4", "10"]
为什么此方法在foldl
上可以正常使用,而在foldr
上却不能正常使用?
答案 0 :(得分:0)
答案:
此差异出现在每个函数的二进制函数定义中:
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
在foldl
接受映射b -> a -> b
的函数的地方,foldr
接受以相反顺序a -> b -> b
接受参数的函数,因此我的step
函数简单地具有改写为step'
(或素数):
step :: [Char] -> [Int] -> [Int] --not the first two reversed parameters
step stack str
| str == "*" = (x*y):remaining
| str == "+" = (x+y):remaining
| str == "-" = (x-y):remaining
| str == "/" = (x `div` y):remaining
| otherwise = (read str :: Int):stack
where y = head $ tail stack -- note y and x are now flipped
x = head stack
remaining = tail $ tail stack
导致文件夹按预期工作:
foldr step' [] ["-", "4", "10"]
> -6
上找到了解释此差异的最有用的资源
右折,折页的工作方式与左折相似,只有累加器会从右折起。同样,左折的二进制函数将累加器作为第一个参数,将当前值作为第二个参数(所以\ acc x-> ...),右折的二进制函数将当前值作为第一个参数,并将累加器作为第二个(因此\ x acc-> ...)。右折在右侧具有累加器,这是有道理的,因为它是从右侧折叠的。