haskell,是否可以调试解析错误?

时间:2014-12-21 01:14:10

标签: 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 = []

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

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

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

我现在所知道的是,这是错误的...出于某种原因(即使某个善良的人告诉我在这种情况下我的错误是什么,我也不想因为这么微小的不同而再次难过未来的问题。)

3 个答案:

答案 0 :(得分:4)

据我所知,没有办法强迫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]

答案 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长和1长的列表。

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为2时,我们必须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]仍然存在错误,我认为这应该是预期的答案。我想你现在应该能够自己修复它。

答案 2 :(得分:1)

嗯,我知道你想要一个方法,而不是答案,但让我从答案开始......错误是你要添加额外的括号。在定义的模式匹配方面,parens意味着与右侧非常不同的东西(即 - 它们意味着'这里的所有内容恰好与一个参数匹配)。移除外层,问题就消失了。

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

这就是事情 - Parse错误是非常基本的,一旦你用语言写了很短的时间,它们很容易被发现。真正有趣的错误(这会让你夜不能寐)几乎不会解析错误。