当`function [] _ = ...时,模式匹配失败; 函数_ [] = ...`语法被省略

Nic*_*año 3 haskell pattern-matching non-exhaustive-patterns

虽然disjoint在保护条件下耗尽了所有可能的模式,但Haskell PatternMatchFail在运行时会给我一个错误.

disjoint :: (Ord a) => [a] -> [a] -> Bool
disjoint l@(x:xs) r@(y:ys)
    | null l || null r   = True
    | x == y             = False
    | x > y              = disjoint l ys -- reduce right list
    | otherwise          = disjoint xs r -- reduce left list
-- | Terminates when either list has been reduced to null, or when their head
-- elements are equal. Since lists are ordered, it only needs to compare head elements.
Run Code Online (Sandbox Code Playgroud)

但是,如果我写,它没有问题:

disjoint :: (Ord a) => [a] -> [a] -> Bool
disjoint [] _ = True
disjoint _ [] = True
disjoint l@(x:xs) r@(y:ys)
--  | null l || null r   = True -- now redundant, but included for sake of continuity
    | x == y             = False
    | x > y              = disjoint l ys -- reduce right list
    | otherwise          = disjoint xs r -- reduce left list
Run Code Online (Sandbox Code Playgroud)

没有那些额外的线,我得到了PatternMatchFail.如果我要在第一种情况下推断出Haskell的问题是什么,那么如果为输入参数提供了一个空列表,那么它的预期参数l@(x:xs) r@(y:ys)已经在调用一个模式匹配,在这种情况下它是非详尽的.一个空列表,导致a PatternMatchFail,尽管有一个保护条件,检查完全相同的条件.它只是无法达到保护条件,因为它首先需要匹配"参数条件".

然而,那些额外的两行在重复性方面对我来说有点令人反感,我只是想知道是否有更简洁的方法来解决这个问题.更一般地说:如果我使用三个或更多列表作为参数,我绝对不想写出不相交3次以上只是为了检查空条件,那么在这样的情况下我可以做些什么呢?感谢您的时间.

ben*_*ofs 12

你解释为什么这会导致模式匹配失败是正确的.您可以通过以下方式编写代码以避免冗余行:

disjoint :: (Ord a) => [a] -> [a] -> Bool
disjoint l@(x:xs) r@(y:ys)
    | x == y             = False
    | x > y              = disjoint l ys -- reduce right list
    | otherwise          = disjoint xs r -- reduce left list
disjoint _ _ = True -- catch all pattern, executed if either l or r is []
Run Code Online (Sandbox Code Playgroud)

这是我推荐的解决方案.还有另一种解决方案,使模式匹配更加懒惰(只有在x/ xsy/ ys实际需要时才检查模式):

disjoint :: (Ord a) => [a] -> [a] -> Bool
disjoint l@ ~(x:xs) r@ ~(y:ys) -- the ~ here says that this is an irrefutable pattern, which makes the match more lazy
    | null l || null r   = True -- x/y is not required, so pattern not checked
    | x == y             = False
    | x > y              = disjoint l ys -- reduce right list
    | otherwise          = disjoint xs r -- reduce left list
Run Code Online (Sandbox Code Playgroud)

我不建议这样做,因为null明确地检查并不像惯用的Haskell(同样,很少使用无可辩驳的模式).第二种方法的问题是你必须注意你不能访问y/ys/ x/xs在null情况下,编译器将无法帮助你.第一种方法保证在null情况下无法访问它们.