3

ガード条件で考えられるすべてのパターンを使い果たし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 の問題が何であるかを推測すると、入力引数に null リストが与えられた場合、期待される引数l@(x:xs) r@(y:ys)がすでにパターンマッチを呼び出しているということです。PatternMatchFailまったく同じ条件をチェックするガード条件があるにもかかわらず、null リストになり、結果は になります。最初に「引数条件」で一致する必要があるため、ガード条件に到達することはできません。

ただし、これらの追加の 2 行は、繰り返しが多いので少し不快であり、これを修正するためのより簡潔な方法があるかどうか疑問に思っていました。より一般的には、引数として 3 つ以上のリストを使用する場合、null 条件をチェックするためだけに 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/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 のように感じられないため、これを行うことはお勧めしません (また、反駁できないパターンはめったに使用されません)。y/ys2 番目のアプローチの問題は、null の場合に/にアクセスしないように注意する必要が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 に答える