为什么会出现此类型错误?

时间:2018-05-16 15:16:18

标签: haskell fold

solveRPN :: String -> Double
solveRPN rpnString = head foldl . foldingFunction [] . words
    where foldingFunction (x:y:ys) "*" = (x * y):ys
          foldingFunction (x:y:ys) "+" = (x + y):ys
          foldingFunction (x:y:ys) "-" = (x - y):ys
          foldingFunction xs numberString = read numberString:xs

以上代码会产生以下错误。我已尝试将String更改为[Char]并替换单引号的双引号,但我一直收到此错误:

_01.hs:874:50: error:
    • Couldn't match type ‘Char’ with ‘[Char]’
      Expected type: String
        Actual type: Char
    • In the first argument of ‘read’, namely ‘numberString’
      In the first argument of ‘(:)’, namely ‘read numberString’
      In the expression: read numberString : xs
    | 874 |           foldingFunction xs numberString = read numberString:xs
    |                                                  ^^^^^^^^^^^^ Failed, 1 module loaded.

2 个答案:

答案 0 :(得分:4)

“了解你的Haskell”一书中的原始解决方案如下所示

solveRPN :: (Num a, Read a) => String -> a  
solveRPN = head . foldl foldingFunction [] . words  
    where   foldingFunction (x:y:ys) "*" = (x * y):ys  
            foldingFunction (x:y:ys) "+" = (x + y):ys  
            foldingFunction (x:y:ys) "-" = (y - x):ys  
            foldingFunction xs numberString = read numberString:xs

当我将其与您的代码段进行比较时,有两件事会导致错误:

1)您错误地将构图链中的合成运算符放错了地方。而不是这个

head foldl . foldingFunction [] . words

你需要这个

head . foldl foldingFunction [] . words

您可以在调用head后使用foldingFunction折叠后将其视为words,其中每个“之后”都是撰写运算符。

2)您使用了合成,但仍然留下了参数rpnString。如果要使用合成,则必须省略链接的参数。所以你可以省略参数:

solveRPN :: String -> Double
solveRPN = head . foldl foldingFunction [] . words
    where   foldingFunction (x:y:ys) "*" = (x * y):ys
            foldingFunction (x:y:ys) "+" = (x + y):ys
            foldingFunction (x:y:ys) "-" = (y - x):ys
            foldingFunction xs numberString = read numberString:xs

或者您可以省略构图链:

solveRPN :: String -> Double
solveRPN rpnString = head (foldl foldingFunction [] (words rpnString))
    where   foldingFunction (x:y:ys) "*" = (x * y):ys
            foldingFunction (x:y:ys) "+" = (x + y):ys
            foldingFunction (x:y:ys) "-" = (y - x):ys
            foldingFunction xs numberString = read numberString:xs

答案 1 :(得分:2)

错误实际上位于外部表达式中:

solveRPN rpnString = head foldl . foldingFunction [] . words

您在head点击foldl,这意味着我们希望foldl具有某种类型[a -> b],因为head可以获得该列表的第一个元素,该元素用于进一步处理。但foldl :: (b -> a -> b) -> b -> [a] -> b当然不是一个清单。

另一个问题是这个子句有一个你忽略的显式参数rpnString:看起来你做了一个 eta -reduction,但只删除了函数体中的参数,而不是在头脑中。

如果我们解决了这两个错误,我们就有了一个有效的解析器:

solveRPN :: String -> Double
solveRPN = head . foldl foldingFunction [] . words

foldingFunction :: [Double] -> String -> [Double]
foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (x - y):ys
foldingFunction xs numberString = read numberString:xs

但是存在语义错误:请注意x是堆栈的顶部(您添加的最新数字),而y是堆栈的子顶部。因此,如果执行减法,则需要在堆栈上按y - x

solveRPN :: String -> Double
solveRPN = head . foldl foldingFunction [] . words

foldingFunction :: [Double] -> String -> [Double]
foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction xs numberString = read numberString:xs

由于其他运算符是可交换的,因此使用运算符的顺序无关紧要。我们现在获得正确的结果。例如:

Prelude> solveRPN "14 25 * 13 - 2 *"
674.0

一些改进建议:

  1. 使用where子句,这样您只能在本地定义foldingFunction
  2. 将输出类型概括为任何Num类型,在这种情况下,我们可以将解析器重用于所有类型的数字(Int s,Float等等。)。
  3. 您可以封装将字符串"*"映射到乘法的逻辑,从而降低代码的复杂性。