`function [] _ = ... 时模式匹配失败; function _ [] = ...`语法被省略
Pattern Match Fail when `function [] _ = ...; function _ [] = ...` syntax is omitted
尽管 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
,尽管有一个保护条件来检查完全相同的条件。它永远无法达到守卫条件,因为它首先需要匹配 "argument condition".
但是,这两行的重复性让我有点反感,我只是想知道是否有更简洁的方法来解决这个问题。更一般地说:如果我要使用三个或更多列表作为参数,我绝对不想为了检查 null 条件而写出不相交的 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
/xs
或 y
/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
,编译器不会帮助您。第一种方法保证您无法在空情况下访问它们。
另一种避免重复的方法是利用模式 match/guard 失败:
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 回答中第一个代码块的样式是我想要的),但这种通用技术可以派上用场一些情况。
尽管 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
,尽管有一个保护条件来检查完全相同的条件。它永远无法达到守卫条件,因为它首先需要匹配 "argument condition".
但是,这两行的重复性让我有点反感,我只是想知道是否有更简洁的方法来解决这个问题。更一般地说:如果我要使用三个或更多列表作为参数,我绝对不想为了检查 null 条件而写出不相交的 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
/xs
或 y
/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
,编译器不会帮助您。第一种方法保证您无法在空情况下访问它们。
另一种避免重复的方法是利用模式 match/guard 失败:
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 回答中第一个代码块的样式是我想要的),但这种通用技术可以派上用场一些情况。