3

尽管disjoint在其保护条件中耗尽了所有可能的模式,但 HaskellPatternMatchFail在运行它时给了我一个错误。

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.

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

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

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

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

4

2 回答 2

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 []

这是我推荐的解决方案。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

不过我不建议这样做,因为null显式检查并不像惯用的 Haskell(此外,很少使用无可辩驳的模式)。第二种方法的问题是您必须注意不要在 null 情况下访问y/ys/ x/xs,并且编译器不会帮助您。第一种方法保证您无法在 null 情况下访问它们。

于 2015-08-11T09:41:47.013 回答
3

避免重复的另一种方法是利用模式匹配/保护失败:

disjoint :: (Ord a) => [a] -> [a] -> Bool
disjoint l r
    | null l || null r   = True
       -- If the guard above fails, then this pattern match is attempted:
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

这在这里有点矫枉过正,我个人更喜欢显式模式匹配null(bennofs 答案中第一个代码块的样式是我想要的),但这种通用技术在某些情况下可能很方便。

于 2015-08-11T15:28:20.910 回答