【问题标题】:Non-exhaustive patterns error when writing recursive list function [duplicate]编写递归列表函数时出现非详尽模式错误[重复]
【发布时间】:2021-09-06 22:20:33
【问题描述】:

在 Haskell 中,我想编写一个递归函数,对于给定的数字列表,它会更改子列表中每个元素的符号:

list = [[1, 3, 6.7, 7.0], [], [1, 8.22, 9, 0]]

multiply (x:xs) = [n * (-1) | n <- x] : multiply xs

但我得到一个错误:

[[-1.0,-3.0,-6.7,-7.0],[],[-1.0,-8.22,-9.0,-0.0]
*** Exception: learning.hs:26:1-48: 
    Non-exhaustive patterns in function multiply

谁能告诉我,如何处理空子列表的异常?

【问题讨论】:

  • 我正在浏览您的个人资料,您的最后一个问题是在 Prolog 中。你在并行学习 Haskell 和 Prolog 吗?如果是这样,我为这项努力鼓掌,因为这两者都不是一件容易的事,即使是单独完成。

标签: haskell non-exhaustive-patterns


【解决方案1】:

实际上问题不是空的子列表

ghci> multiply [[1, 2, 3], [4, 5, 6]]
[[-1,-2,-3],[-4,-5,-6]*** Exception: <interactive>:2:1-50: Non-exhaustive patterns in function multiply

您使用[n * (-1) | n &lt;-x] 处理子列表,列表推导在空列表上工作正常;这个只会发现没有元素可以乘以-1,因此会产生一个空列表。您可以在引用的输出中看到空列表;之后它甚至会继续产生更多的输出,这很确定它在处理空子列表时没有抛出异常。

那么问题是什么?好吧,让我们看看错误消息实际上是怎么说的:Non-exhaustive patterns in function multiply。这意味着您的函数multiply 中的某处代码正在执行模式匹配,而被匹配的值实际上并不适合您提供的任何模式。嗯,在你的整个函数中只有一个地方可以进行任何模式匹配,那就是multiply (x: xs),所以这一定是问题所在!

现在列表总是空列表[]item : rest_of_list 形式(其中: 是列表数据类型的另一个构造函数)。您: 表单提供了一个模式,因此如果您将multiply 应用于空列表,肯定会抛出错误。这立即向我们展示了问题,即使没有将其与您的案例中实际发生的情况联系起来(我稍后会做)。

如何解决?您需要说出multiply [] 应该产生什么结果。通常,当您编写列表的递归函数时,您希望以这种形式开始:

func [] = _
func (x : xs) = _

然后填空。如果您需要专门处理具有 1 个或 2 个元素的列表,有时使用 [x][x, y] 等其他情况会很方便。只有极少数情况下您应该忽略 [] 的情况,因为如果您这样做,您的函数将肯定抛出您在某些调用的问题中看到的异常。

在这种情况下,multiply 只是将其 list-of-list 参数的所有子列表中的所有值取反,因此很容易看出它应该对 empty 列表做什么子列表:只返回一个空列表。所以我们会有:

multiply [] = []
multiply (x: xs) = [n * (-1) | n <-x]: multiply xs

(如果您将其输入 GHCi 而不是文件,则需要使用多行模式来输入;输入 :{ 以启动多行模式,然后输入定义的所有行,然后输入:} 一次将所有代码行提供给编译器)


现在,还有一个更明显的问题。你没有打电话给multiply [],你打电话给multiply [[1,3,6.7,7.0],[],[1,8.22,9,0]]。那么为什么它抱怨参数与 empty 列表的模式不匹配?原来的调用没有,但每次你调用原来的multiply 时,它都会在一个较小的列表中调用自己!

它将[ [1,3,6.7,7.0], [], [1,8.22,9,0] ]x : xs 模式匹配,从而产生x = [1,3,6.7,7.0]xs = [ [], [1,8.22,9,0] ]。然后它调用multiply xs

所以在第二次调用中,我们将[ [], [1,8.22,9,0] ]x : xs 模式进行匹配。在本次评估中,x = []xs = [ [1,8.22,9,0] ]。我们用这个版本的xs 再次调用multiply xs

现在在第三次调用中,我们将 [ [1,8.22,9,0] ] 与模式 x : xs 匹配。为了成功,我们必须找到 both xxs。单个元素列表的尾部是空列表,所以x = [1,8.22,9,0]xs = []。然后我们用这个版本的xs 再次调用multply xs,这就是问题所在。现在我们尝试将[]x : xs 模式匹配,但我们不能,但也没有其他模式可以尝试,所以我们只得到Non-exhaustive patterns in function multiply

【讨论】:

    【解决方案2】:

    您在处理递归步骤方面做得非常出色。但与任何编程语言一样,我们还需要一个基本案例来终止递归。具体来说,您需要为[] 提供一个明确的案例。如果给定[],您希望您的函数返回[],因为没有更多工作要做。考虑

    multiply :: Num a => [[a]] -> [[a]]
    multiply [] = []
    multiply (x : xs) = [n * (-1) | n <- x] : multiply xs
    

    底线正是您已经编写的内容。您需要第二行来处理[] 输入。第一行是类型签名,虽然不是严格要求,但在 Haskell 中是很好的设计,如果出现问题会产生更好的错误消息。

    【讨论】:

      【解决方案3】:

      你在列表的尾部递归,这意味着最终你将调用 multiply xsxs 一个空列表,并且由于你没有为空列表指定大小写,它会引发错误。

      如果我们因此使用列表[1,4] 运行代码,我们将使用multiply [1,4] 进行调用,这将使用multiply [4] 进行递归调用,然后使用[] 进行递归调用,然后它找不到与空列表匹配的模式,因此出现错误。因此,这意味着您的函数缺少基本情况。我们可以这样实现:

      multiply :: Num a => [[a]] -> [[a]]
      multiply [] = []
      multiply (x: xs) = [n * (-1) | n <-x] : multiply xs

      然而我们不需要使用递归,事实上我们可以用map :: (a -&gt; b) -&gt; [a] -&gt; [b]negate :: Num a =&gt; a -&gt; a来实现:

      multiply :: Num a => [[a]] -> [[a]]
      multiply = map (map negate)

      或者我们甚至可以将其进一步推广到任何Functor

      multiply :: (Foldable f, Foldable g, Num a) => f (g a) -> f (g a)
      multiply = fmap (fmap negate)

      【讨论】:

        【解决方案4】:

        您可以将 map 与包含相同列表理解的 lambda 函数一起使用:

        Prelude> list=[[1,3,6.7,7.0],[],[1,8.22,9,0]]
        Prelude> map (\x -> [n * (-1) | n <-x]) list
        [[-1.0,-3.0,-6.7,-7.0],[],[-1.0,-8.22,-9.0,-0.0]]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多