当' function [] _ = ..;函数_ []= ..语法省略



虽然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.

但是,如果我写:

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,尽管有一个检查完全相同条件的保护条件。它永远无法达到保护条件,因为它首先需要匹配"参数条件"。

然而,那额外的两行对我来说是一个有点讨厌的重复,我只是想知道是否有一个更简洁的方法来解决这个问题。更一般地说:如果我要使用三个或更多的列表作为参数,我肯定不想为了检查空条件而多次写出不相交的3+,那么在这种情况下我该怎么做呢?谢谢你的宝贵时间。

您对模式匹配失败的解释是正确的。您可以按照以下方式编写代码以避免冗余行:

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情况下不能访问它们。

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

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答案中第一个代码块的风格是我想要的),但是这种通用技术在某些情况下很方便。

相关内容

  • 没有找到相关文章

最新更新