Я только начинаю Haskell, но из всех учебных руководств онлайн я нашел, что, может казаться, не нахожу, существует ли принятый способ того сделать, условное выражение проверяет утверждение. Я видел, если еще, защита и сопоставление с образцом, но они все, кажется, выполняют то же самое. Есть ли один общепринятый/faster/more эффективный путь, чем остальные?
Существует ли один общепринятый/быстрый/более эффективный способ, чем остальные?
Стражи - это (довольно сложный) синтаксический сахар для if-then-else, следующего за сопоставлением шаблонов. If-then-else - это синтаксический сахар для case
над Bool
. Так что эти вещи в основном одинаково эффективны.
Но вот наблюдение: часто бывает легко сделать неэффективно с помощью булева выражения то, что эффективно с помощью совпадения шаблонов. Любимый пример начинающих программистов на Haskell - написать
length xs == 0
что стоит пропорционально длине xs
, где
case xs of { [] -> True; _:_ -> False }
стоит постоянное время.
Более точный способ понять, что происходит, заключается в том, что (при отсутствии причудливых расширений вроде шаблонов представления) наихудшая стоимость сопоставления шаблонов пропорциональна количеству конструкторов, появляющихся в левой части - вы просто не можете написать сопоставление шаблонов, которое было бы одновременно дорогим и маленьким. В отличие от этого, размер булева выражения не говорит вам ничего о том, сколько стоит его оценка. В этом смысле, и только в этом смысле, согласование по образцу дешевле, чем if-then-else или guards.
Хорошая эвристика для начинающих - использовать согласование шаблонов везде, где только можно. По мере накопления опыта вы сможете усовершенствовать свой подход.
Ни один из трех вариантов не выполняет одно и то же действие и не может использоваться во всех ситуациях.
Соответствие шаблону проверяет, какой конструктор был использован для создания заданного значения, и они связывают переменные. Ни если, ни охрана этого не делают. Вы можете использовать их вместо сопоставления с шаблоном, только если вы сопоставляете конструктор с нулевым значением (или числовой литерал) типа, реализующего Eq.
Пример:
foo (Just x) = x+1 -- Can not do this without a pattern match (except by using
-- functions like fromJust that themselves use pattern matches)
foo Nothing = 0 -- You could do this using a pattern guards like
-- foo x | x==Nothing = 0, but that is less readable and less
-- concise than using a plain pattern match
Шаблонные стражи позволяют вам проверять другие вещи, кроме равенства. Например. вы можете проверить, больше ли данное число нуля. Конечно, вы можете сделать то же самое с if, но защита шаблонов позволяет вам перейти к следующему шаблону, когда защита выходит из строя, что может привести к меньшему повторению, чем использование if. Пример:
maybeSqrt (Just x) | x >= 0 = sqrt x
maybeSqrt _ = Nothing
Использование if, это будет выглядеть так (обратите внимание на повторение Nothing):
maybeSqrt (Just x) = if x >= 0 then sqrt x
else Nothing
maybeSqrt _ = Nothing
Наконец, if можно использовать без совпадений с образцом. Если вы на самом деле не используете сопоставление с образцом для заданного значения, введение case x of ...
просто для того, чтобы вы могли использовать защиту от образца, не имеет смысла, менее читаемо и кратко, чем просто использование if.
Я выбираю, основываясь на том, что делает код красивее и легче для чтения. Как отмечает @Don, многие из этих различных форм компилируются в case
. Они выглядят по-разному из-за синтаксического сахара, который доступен. Этот сахар не для компилятора, а для людей. Поэтому принимайте решение, основываясь на том, что, по вашему мнению, хотели бы прочитать другие люди, и что кажется читабельным вам.
Ну, я не знаю, является ли мышление в терминах «управляющих утверждений» лучшим способом об этом в Haskell. Тем не менее, в конечном итоге все сводится к сопоставлению с образцом; логические условия, такие как if ... then ... else
, могут быть определены в терминах сопоставления с образцом в конструкторах, например, для Bool
.
Наиболее "примитивной" формой, вероятно, является оператор case
- сопоставление с образцом для определений функций - это просто синтаксический сахар над одним определением функции, содержащим одно большое выражение case
.
С точки зрения того, что вам следует использовать, выбирайте то, что концептуально является наиболее разумным. Сопоставление с образцом наиболее подходит, когда вам нужно разобрать алгебраический тип данных; Блоки if
подходят, когда вам нужен простой результат да / нет для некоторого предиката. Защиты обычно используются, когда вам нужно сочетание деконструкции типа данных и логических предикатов.
Наиболее важным моментом, о котором следует помнить, является то, что сопоставление с образцом - единственный способ разобрать алгебраический тип данных . Логические предикаты можно легко заменить функциями высшего порядка , но для извлечения значений внутри конструктора данных требуется сопоставление с образцом.