Haskell为什么foldl接受我的功能而foldr拒绝我的功能

时间:2019-01-05 10:07:30

标签: haskell fold

我定义了两个函数,它们充当反向抛光表示法评估器(后缀评估器):

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上却不能正常使用?

1 个答案:

答案 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

following link

上找到了解释此差异的最有用的资源
  

右折,折页的工作方式与左折相似,只有累加器会从右折起。同样,左折的二进制函数将累加器作为第一个参数,将当前值作为第二个参数(所以\ acc x-> ...),右折的二进制函数将当前值作为第一个参数,并将累加器作为第二个(因此\ x acc-> ...)。右折在右侧具有累加器,这是有道理的,因为它是从右侧折叠的。