Я только начинаю Haskell, но из всех онлайн-руководств, которые я нашел, я не могу найти, если есть один принятый способ сделать условный оператор управления. Я видел if-else, охранники и сопоставление образцов, но все они, похоже, выполняют одно и то же. Есть ли общепринятый/более быстрый/более эффективный способ, чем остальные?
Контрольные заявления в Haskell?
Ответ 1
Есть ли общепринятый/более быстрый/более эффективный способ, чем остальные?
Гвардейцы (довольно сложный) синтаксический сахар для if-then-else после сопоставления с образцом. If-then-else - синтаксический сахар для case
над Bool
. Таким образом, эти вещи в основном одинаково эффективны.
Но здесь наблюдение: часто бывает легко сделать неэффективно с булевым выражением, что эффективно с совпадением шаблонов. Излюбленным примером начинающих программистов Haskell является запись
length xs == 0
который стоит пропорционально длине xs
, где
case xs of { [] -> True; _:_ -> False }
стоит постоянное время.
Более точный способ наблюдать за тем, что происходит (отсутствие фантастических расширений, таких как шаблоны представлений), наихудшая стоимость сопоставления шаблонов пропорциональна числу конструкторов, появляющихся в левой части -— вы просто можете 't написать соответствие шаблонов, которое является как дорогостоящим, так и маленьким. Напротив, размер булевского выражения ничего не говорит о том, что стоит оценить. В этом смысле и в этом смысле только сопоставление образцов дешевле, чем если-то-иначе или охранники.
Хорошая эвристика для начинающих - это использовать сопоставление шаблонов, где бы вы ни находились.. Когда вы получите больше опыта, вы можете уточнить свой подход.
Ответ 2
Ну, я не знаю, является ли мышление с точки зрения "контрольных заявлений" лучшим способом обойти это в Haskell. Тем не менее, в основном все сводится к совпадению шаблонов в конце; булевские условные выражения, такие как if ... then ... else
, могут быть определены в терминах сопоставления шаблонов для конструкторов для Bool
, например.
Наиболее "примитивной" формой, вероятно, является оператор case
- соответствие шаблона для определений функций - это просто синтаксический сахар над определением одной функции, содержащим одно большое выражение case
.
В терминах того, что вы должны использовать, идите с тем, что имеет смысл в концептуальном смысле. Совпадение шаблонов наиболее подходит для того, чтобы разделить тип алгебраических данных; Блоки if
подходят для того, когда вам нужен простой результат да/нет для некоторого предиката. Гвардейцы обычно используются, когда вам нужна смесь деконструкции типа данных и булевых предикатов.
Самый важный момент, который следует учитывать, заключается в том, что сопоставление шаблонов - единственный способ разделить алгебраический тип данных. Булевы предикаты могут быть легко заменены функциями более высокого порядка, но для извлечения значений внутри конструктора данных требуется сопоставление образцов.
Ответ 3
Ни один из трех вариантов не делает то же самое или может использоваться во всех ситуациях.
Шаблоны соответствия проверяют, какой конструктор использовался для создания заданного значения, и привязывают переменные. Ни то, ни другое охранники не делают этого. Вы могли использовать их только вместо совпадений шаблонов, если вы сопоставляете с конструктором с нулевым значением (или литералом числа) типа, реализующего 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
Использование, если это будет выглядеть так (обратите внимание на повторение Nothing):
maybeSqrt (Just x) = if x >= 0 then sqrt x
else Nothing
maybeSqrt _ = Nothing
Наконец, если их можно использовать без совпадений шаблонов. Если вы фактически не используете сопоставление шаблонов по заданному значению, введя case x of ...
, чтобы вы могли использовать шаблоны защиты, имеет мало смысла и менее читабельны и кратки, чем просто использовать if.
Ответ 4
Я выбираю на основе того, что делает код более красивым и понятным. Как отмечает @Don, многие из этих разных форм скомпилированы до case
. Они выглядят по-разному из-за имеющегося синтаксического сахара. Этот сахар не для компилятора, это для людей. Поэтому решите, исходя из того, что вы думаете, что другие люди хотели бы прочитать, и того, что выглядит читаемым для вас.