【问题标题】:haskell, is it possible to debug a parse error?haskell,是否可以调试解析错误?
【发布时间】:2014-12-21 01:14:09
【问题描述】:

我正在做一些练习,我发现伴随着一组在线讲座,遇到了一个我无法克服的问题,基本上,我在文件中定义了一个函数

doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
doubleEveryOtherHelper ((x:x1:xs) len)
    | len > 2   = x : 2*x1 : doubleEveryOtherHelper(xs (len - 1))
    | len == 2 = x : 2*x1
    | len == 1 = [2*x]
    | otherwise = []

加载文件后,我得到了这个(而且只有这个):

Parse error in pattern: (x : x1 : xs)

无论如果我设法加载该函数可能会或可能不会做什么,有没有办法让 ghci 告诉我它试图以人类可读的形式解析的所有内容,以便我可以找出这些东西我自己(即如果我能看到它实际上试图解析的是什么(可能是垃圾),而不是我认为它试图解析的东西(一个函数名和两个参数:一个列表和一个 Int,然后返回取决于 len 的值的列表)然后修复它应该容易得多)?

我现在只知道这是错误的......出于某种原因(即使某个好心人告诉我在这种情况下我的错误是什么,我也不想再次被如此细微的不同所困扰未来的问题。)

【问题讨论】:

    标签: haskell


    【解决方案1】:

    据我所知,没有办法强迫 GHCI 向您提供更多信息(尽管我承认我不是一个非常有经验的 GHCI 用户)。 Haskell 错误可能非常神秘,但识别和理解它们的能力将随着时间和经验而来。同时,我可以为您提供一些关于理解和修复该错误的提示:

    • Parse error 这意味着 GHCI 无法理解您的代码的字典结构,无论您编写的代码应该做什么。解析错误的常见原因包括不一致的缩进和不匹配的分隔符,例如额外的或缺少结束 )。这让您知道您正在使用的顶级语法存在问题,具体来说:

    • in pattern: 由于您在代码中使用它,我想您应该熟悉pattern matching。这让我们知道您在模式表达式中使用的语法存在问题。具体来说,围绕这个:

    • (x : x1 : xs) 这是匹配两个或多个元素列表的有效模式。我们知道这种结构会导致错误,但由于它本身是有效的,因此我们可以得出结论,这种模式由于使用它的上下文而导致了解析错误。

    扩大我们的视野以查看整个模式,我们发现一对无关的括号围绕着正在抛出解析器的模式:

    (   (x:x1:xs) len   ) --These should not be here
    

    由于(x:x1:xs)len 不是同一个数据结构的一部分,它们不应该放在括号中(Haskell 在这方面与许多语言不同,函数调用中的所有参数都用括号括起来) .删除这些括号可修复解析错误...

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper (x:x1:xs) len
        | len > 2   = x : 2*x1 : doubleEveryOtherHelper(xs (len - 1))
        | len == 2 = x : 2*x1
        | len == 1 = [2*x]
        | otherwise = []
    

    ...但是在我们现在的词典中(但在逻辑上不正确的函数)中发现了大量其他错误。祝调试愉快!

    编辑:将所有这些新错误留给你似乎很不礼貌,所以我修复了它们。这是一个可以正确编译的函数(虽然我还没有测试它是否真的有效):

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper (x:x1:xs) len --no parens around pattern
        --fixed parens to account for operator precedence
        | len > 2   = x : (2*x1) : (doubleEveryOtherHelper xs (len - 1)) --removed parens around call to doubleEveryOtherHelper, same error as before
        | len == 2 = x : [2*x1]
        | len == 1 = [2*x]
        | otherwise = []
    

    请注意,第 3 行和第 4 行可以写得更清楚:

     | len > 2  = [x, 2*x1] ++ doubleEveryOtherHelper xs (len - 1)
     | len == 2 = [x, 2*x1]
    

    【讨论】:

    • 特别注意递归调用doubleEveryOtherHelper中的相同错误
    • 谢谢,这是一个非常有用的答案(并指出我应该更详细地阅读的方向)。
    • 您实际上并不需要所有这些括号:x : 2*x1 : doubleEveryOtherHelper xs (len - 1) 可以。此外,x : [2*x1] 更常写作[x, 2*x1]
    • @chi 当然!我不敢相信我错过了。谢谢,这是一种更清晰的表达方式。
    • | len > 2 = ([x, 2*x1] ++ doubleEveryOtherHelper xs) $ (len - 1) 可能不是你想要的......
    【解决方案2】:

    其他人已经指出了解析错误。让我在这段代码中的逻辑错误上添加一些东西,即使没有明确要求。

    -- Syntactically fixed code
    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper (x:x1:xs) len
        | len > 2   = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
        | len == 2 = x : 2*x1 : []
        | len == 1 = [2*x]
        | otherwise = []
    

    我猜len 参数应该是整数列表的长度。

    现在,x:x1:xs 模式匹配第一个元素为 x,第二个元素为 x1 的列表。因此,该模式将 从不 与少于两个元素的列表匹配。这意味着对len==1<1 的测试将始终失败。

    这样做的惯用方法是指定要匹配的多个模式。你的匹配至少有 2 个元素长的列表,所以让我们用两种模式覆盖 0-long 和 1-long 列表。

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper []        len = ???
    doubleEveryOtherHelper [x]       len = ???
    doubleEveryOtherHelper (x:x1:xs) len
        | len > 2   = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
        | len == 2 = x : 2*x1 : []
        | len == 1 = [2*x]
        | otherwise = []
    

    那么,让我们用底部的代码填充那些???

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper []        len = []    -- was in the oterwise case
    doubleEveryOtherHelper [x]       len = [2*x] -- was in the len==1 case
    doubleEveryOtherHelper (x:x1:xs) len
        | len > 2   = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
        | len == 2 = x : 2*x1 : []
    

    现在,关注最后一行。当len 为二时,我们必须有xs==[]。在这种假设下,我们有doubleEveryOtherHelper xs anyLength == [],因此我们可以通过将最后一行中的[] 替换为等效的doubleEveryOtherHelper xs (len - 1) 来使他的代码更复杂。为什么我们要让代码更复杂?!?嗯,这样的结果是

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper []        len = []    -- was in the oterwise case
    doubleEveryOtherHelper [x]       len = [2*x] -- was in the len==1 case
    doubleEveryOtherHelper (x:x1:xs) len
        | len > 2  = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
        | len == 2 = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
    

    很明显,最后两种情况可以使用相同的代码来处理:

    doubleEveryOtherHelper :: [Integer] -> Int ->  [Integer]
    doubleEveryOtherHelper []        len = []   
    doubleEveryOtherHelper [x]       len = [2*x]
    doubleEveryOtherHelper (x:x1:xs) len = x : 2*x1 : doubleEveryOtherHelper xs (len - 1)
    

    现在,我们根本不使用len 参数。因此,如果我们同意在调用站点上删除它,我们可以将其删除。

    doubleEveryOtherHelper :: [Integer] -> [Integer]
    doubleEveryOtherHelper []        = []   
    doubleEveryOtherHelper [x]       = [2*x]
    doubleEveryOtherHelper (x:x1:xs) = x : 2*x1 : doubleEveryOtherHelper xs
    

    简单多了,不是吗?

    请注意,doubleEveryOtherHelper [1,2,3] = [1,4,6] 而不是[1,4,3] 中仍然存在一个错误,我认为这应该是预期的答案。我认为您现在应该可以自己修复它了。

    【讨论】:

    • 感谢这个...在编译完我的代码并玩了一会后,我发现我采用的方法并不是最佳的,实际上我发现自己采用了与这里基本相同的方法(尽管与 [x] 的匹配返回 [x] 而不是 [2*x])。这是一篇很好的文章
    【解决方案3】:

    好吧,我知道你想要一个方法,而不是答案,但让我从答案开始......错误是你添加了额外的括号。在定义的模式匹配方面,parens 的含义与右侧非常不同(即,它们的意思是“这里的所有内容都与一个参数完全匹配)。去掉外括号,问题就消失了。

    至于更广泛的问题,我实际上并不知道如何查看语法预期的答案,但如果它确实存在,它可能没有你想象的那么有用。解析错误是最基本的错误类型,只有在不满足语言语法时才会触发。本质上,当这种情况发生时,编译器根本不知道该怎么做。从错误报告的角度来看,编译器真正能做的就是在错误发生时向您显示输入,并可能向您显示可能预期的值列表。后者的问题在于,它往往有点过于冗长而且没有真正的用处......

    事情是这样的——解析错误是非常基本的,一旦你用这种语言编写了很短的时间,它们就很容易被发现。真正有趣的错误(会让您彻夜难眠)几乎从不解析错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-21
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多