我正在通过一些练习在网上找到一些练习,并且遇到了一个我无法克服的问题,本质上,我在文件中定义了一个函数
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的值的列表然后修复它应该会容易多了)?
我现在所知道的是,这是错误的...出于某种原因(即使某个善良的人告诉我在这种情况下我的错误是什么,我也不想因为这么微小的不同而再次难过未来的问题。)
答案 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错误是非常基本的,一旦你用语言写了很短的时间,它们很容易被发现。真正有趣的错误(这会让你夜不能寐)几乎不会解析错误。