2015-08-11 5 views
3

Хотя disjoint исчерпывает все возможные шаблоны в своих режимах защиты, Haskell дает мне ошибку PatternMatchFail при запуске.Ошибка совпадения шаблонов, когда `function [] _ ​​= ...; function _ [] = ... `синтаксис опущен

disjoint :: (Ord a) => [a] -> [a] -> Bool 
disjoint [email protected](x:xs) [email protected](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 [email protected](x:xs) [email protected](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 в первом случае, это то, что если заданный нулевой список для входного аргумента, его ожидаемые аргументы [email protected](x:xs) [email protected](y:ys) уже вызывают совпадение шаблонов, которое не является исчерпывающим в случае нулевого списка, что приводит к PatternMatchFail, несмотря на наличие состояния защиты, которое проверяет точно такое же условие. Он просто не может достигнуть состояния охраны, потому что ему сначала нужно сопоставить «условие аргумента».

Тем не менее, эти дополнительные две линии являются излишним отбрасыванием для меня в их повторяемости, и мне просто интересно, был ли более сжатый способ исправить это. В более общем плане: если бы я использовал три или более списков в качестве аргументов, я определенно не хотел бы выписывать disjoint 3+ больше раз, чтобы проверить нулевые условия, так что я могу сделать в таких случаях? Спасибо за ваше время.

+4

well '(x: xs)' не будет совпадать с пустым списком, очевидно ... так что ваш первый пример просто не исчерпывающий - ** охранники ** только пинают * после * сопоставленного шаблона (в качестве дополнительного. .. well guard - подумайте об этом как 'disjoint l @ (x: xs) ... = если null l then ...') – Carsten

ответ

12

Ваше объяснение, почему это дает ошибку соответствия шаблона, является правильным. Вы можете написать код следующим образом, чтобы избежать избыточных линий:

disjoint :: (Ord a) => [a] -> [a] -> Bool 
disjoint [email protected](x:xs) [email protected](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 [email protected] ~(x:xs) [email protected] ~(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/ys/x/xs в нулевых случаях, и компилятор вам не поможет. Первый подход гарантирует, что вы не можете получить к ним доступ в нулевых случаях.

+0

Идеальный ответ. Большое спасибо. Я не понимал, что вы можете добавить непересекающиеся _____ = статьи после охранников. –

+0

Для вашего второго решения вам нужно пространство между '@' и '~', иначе оно думает, что это другой оператор. –

+0

@ ØrjanJohansen спасибо, я не знал об этом, я отредактирую сообщение – bennofs

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 [email protected](x:xs) [email protected](y:ys) 
    | x == y    = False 
    | x > y    = disjoint l ys -- reduce right list 
    | otherwise   = disjoint xs r -- reduce left list 

Это избыточна здесь и лично я предпочитаю явное соответствие шаблону над null (стиль первого кода блок в bennofs ответ - это то, на что я бы пошел), но эта общая техника может быть удобной в некоторых ситуациях.