Мое эмпирическое правило будет таким:
- Используйте сопоставление с образцом, когда охранник будет простой
==
проверкой.
При рекурсии вы обычно проверяете базовый вариант. Поэтому, если ваш базовый случай - простая проверка ==
, используйте сопоставление с образцом.
Так что я обычно делаю это:
map f [] = []
map f (x:xs) = f x : map f xs
Вместо этого (null
просто проверяет, является ли список пустым. В основном это == []
):
map f xs | null xs = []
| otherwise = f (head xs) : map f (tail xs)
Сопоставление с образцом призвано сделать вашу жизнь проще, имхо, поэтому в конце вы должны сделать то, что имеет для вас смысл. Если вы работаете с группой, делайте то, что имеет смысл для группы.
[обновление]
Для вашего конкретного случая я бы сделал что-то вроде этого:
f _ [] = []
f 0 _ = ...
f y (x:xs) = ...
Матричные соответствия, как и охранники, падают сверху вниз, останавливаясь на первом определении, соответствующем вводу. Я использовал символ подчеркивания, чтобы указать, что для первого совпадения с шаблоном мне было все равно, каков аргумент y
, а для второго совпадения с шаблоном мне было все равно, что это за аргумент списка (хотя, если вы используйте список в этом вычислении, тогда вы не должны использовать подчеркивание). Так как это все еще довольно простые ==
-подобные проверки, я лично буду придерживаться сопоставления с образцом.
Но я думаю, что это вопрос личных предпочтений; Ваш код отлично читается и корректен как есть. Если я не ошибаюсь, когда код скомпилирован, в конечном итоге оба элемента защиты и сопоставления с образцом превращаются в операторы case.