Контрольные заявления в Haskell? - PullRequest
11 голосов
/ 07 мая 2010

Я только начинаю Haskell, но из всех найденных мною онлайн-учебников я не могу найти единственного приемлемого способа сделать оператор условного контроля.Я видел if-else, охранников и сопоставление с образцом, но все они, кажется, выполняют одно и то же.Есть ли один общепринятый / более быстрый / более эффективный способ, чем остальные?

Ответы [ 4 ]

11 голосов
/ 07 мая 2010

Есть ли один общепринятый / более быстрый / более эффективный способ, чем остальные?

Охранники являются (довольно сложным) синтаксическим сахаром для последующего сопоставления с образцом. If-then-else является синтаксическим сахаром для case над Bool. Так что эти вещи в основном одинаково эффективны.

Но вот наблюдение: часто легко сделать неэффективно с логическим выражением, что эффективно с сопоставлением с образцом. Любимый пример начинающих программистов на Haskell - писать

length xs == 0

, который стоит пропорционально длине xs, где

case xs of { [] -> True; _:_ -> False }

стоит постоянного времени.

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

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

4 голосов
/ 07 мая 2010

Ни один из трех вариантов не делает одно и то же или не может использоваться во всех ситуациях.

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

Пример:

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 голосов
/ 07 мая 2010

Ну, я не знаю, является ли мышление в терминах «контрольных высказываний» лучшим способом сделать это в Хаскеле.Тем не менее, в основном все сводится к сопоставлению с образцом в конце;Например, логические условия, такие как if ... then ... else, могут быть определены в терминах сопоставления с образцом в конструкторах для Bool.

Наиболее «примитивной» формой, вероятно, является оператор case - сопоставление с образцом для функцииопределения - это просто синтаксический сахар по сравнению с определением одной функции, содержащим одно большое case выражение.

С точки зрения того, что вы должны использовать, подходите к тому, что имеет концептуальный смысл.Сопоставление с образцом наиболее подходит для случаев, когда вам нужно разобрать алгебраический тип данных;Блоки if подходят для случаев, когда вам нужен простой результат да / нет для некоторого предиката.Защита обычно используется, когда вам нужно сочетание деконструкции типа данных и логических предикатов.

Наиболее важным моментом, который следует иметь в виду, является то, что сопоставление с шаблоном - это единственный способ разобрать алгебраический тип данных..Булевы предикаты могут быть легко заменены функциями высшего порядка , но для извлечения значений внутри конструктора данных требуется сопоставление с образцом.

2 голосов
/ 07 мая 2010

Я выбираю исходя из того, что делает код более красивым и удобным для чтения. Как указывает @Don, многие из этих различных форм компилируются в case. Они выглядят по-разному из-за синтаксического сахара, который доступен. Этот сахар не для компилятора, а для людей. Поэтому решайте, основываясь на том, что, по вашему мнению, другие люди хотели бы прочитать, и на том, что вам кажется читаемым.

...