Синтаксис языка Haskell для «или» в выражениях - PullRequest
29 голосов
/ 30 сентября 2011

В F # я могу использовать | для группировки случаев при сопоставлении с образцом.Например,

let rec factorial n = 
  match n with
  | 0 | 1 -> 1                 // like in this line
  | _ -> n * factorial (n - 1)

Каков синтаксис Haskell для того же?

Ответы [ 5 ]

30 голосов
/ 30 сентября 2011

Невозможно разделить одну и ту же правую сторону для разных образцов.Тем не менее, вы обычно можете обойти это, используя охрану вместо шаблонов, например с elem.

foo x | x `elem` [A, C, G] = ...
      | x `elem` [B, D, E] = ...
      | otherwise          = ...
10 голосов
/ 30 сентября 2011

с защитой:

factorial n
    | n < 2 = 1
    | otherwise = n * (factorial (n - 1))

с сопоставлением с шаблоном:

factorial 0 = 1
factorial 1 = 1
factorial n = n * (factorial (n - 1))
8 голосов
/ 04 сентября 2014

Опираясь на некоторые из приведенных выше ответов, вы можете (по крайней мере сейчас) использовать охранников для выполнения нескольких дел в одной строке:

case name of
    x | elem x ["Bob","John","Joe"] -> putStrLn "ok!"
    "Frank"                         -> putStrLn "not ok!"
    _                               -> putStrLn "bad input!"

Итак, ввод "Боб", "Джон"или «Джо» даст вам «хорошо!», тогда как «Фрэнк» будет «не в порядке!», а все остальное будет «плохой ввод!»

8 голосов
/ 30 сентября 2011

Я не совсем знаком с F #, но в Haskell операторы case позволяют вам сопоставлять шаблоны, связывая переменные с частями выражения.

case listExpr of
    (x:y:_) -> x+y
    [x]     -> x
    _       -> 0

В теоретическом случае, что Хаскелл допускает то же самое:

Поэтому было бы проблематично разрешить множественные привязки

case listExpr of
    (x:y:_) | [z] -> erm...which variables are bound? x and y? or z?

В редких случаях это может работать при использовании одной и той же привязки:

 unEither :: Either a a -> a
 unEither val = case val of
   Left v | Right v -> v

И, как в приведенном вами примере, он может работать нормально, если вы только сопоставляете литералы и ничего не связываете:

case expr of
  1 | 0 -> foo
  _     -> bar

Тем не менее:

Насколько я знаю, у Haskell такой синтаксис отсутствует. Однако у него есть стражники, как уже упоминали другие.

Также обратите внимание:

Использование | в операторе case выполняет другую функцию в Haskell. Заявление после | действует как охранник.

case expr of
  [x] | x < 2 -> 2
  [x] -> 3
  _ -> 4

Так что, если такой синтаксис должен быть введен в Haskell, ему придется использовать что-то отличное от |. Я бы предложил использовать , (кому захочется добавить это в спецификацию Haskell.)

unEither val = case val of
  Left v, Right v -> v

В настоящее время выдается «ошибка разбора на входе ,»

3 голосов
/ 15 апреля 2014

Вот довольно буквальный перевод:

factorial n = case n of
    0 -> sharedImpl
    1 -> sharedImpl
    n -> n * factorial (n - 1)
    where
        sharedImpl = 1

Шаблоны просмотра также могут дать вам буквальный перевод.

isZeroOrOne n = case n of
    0 -> True
    1 -> True
    _ -> False

factorial1 n = case n of
    (isZeroOrOne -> True) -> 1
    n -> n * factorial (n - 1)

factorial2 n = case n of
    (\n -> case n of { 0 -> True; 1 -> True; _ -> False }) -> 1
    n -> n * factorial (n - 1)

Не говоря о том, что это лучше, чем альтернативы.Просто указываю на них.

...