Haskell中case表达式中表示“或”的语法

42

在 F# 中,我可以使用|来分组匹配模式。例如:

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

如何在 Haskell 中实现相同的语法?


请参阅Haskell 2010 > 表达式 # Case表达式以获取Haskell case语句的官方规范。 - Dan Burton
6
目前还不支持此功能,但请参考http://www.haskell.org/haskellwiki/MultiCase以获取一份语法提案。 - hvr
请注意,hvr提供的语法建议是直接回应这个StackOverflow问题的。 ;) - Dan Burton
如在 guards 中提到的那样,您可以将 | 符号视为 F# 中的 when 关键字。 - MasterMastic
5个回答

40

无法共享不同模式的相同右手边。但是,通常可以通过使用守卫而不是模式来解决这个问题,例如elem

foo x | x `elem` [A, C, G] = ...
      | x `elem` [B, D, E] = ...
      | otherwise          = ...

使用守卫如何帮助共享等式的右侧? - jmg
它的意思是,由于发帖者的情况是: | x elem [0,1] = 1,因此会这样做。 - Jimmy Hoffa

10

使用守卫:

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

使用模式匹配:

factorial 0 = 1
factorial 1 = 1
factorial n = n * (factorial (n - 1))

如果我有很多情况,比如输出给定月份的天数,该怎么办? - Rahul Göma Phuloré

9

在上述答案的基础上,您可以(至少现在)使用守卫在单行上执行多个情况:

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

因此,输入"Bob"、"John"或"Joe"将给出"ok!"的结果,而"Frank"将是"not ok!",其他所有输入都将是"bad input!"


9

我不完全了解F#,但在Haskell中,case语句允许你进行模式匹配,将变量与表达式的部分绑定在一起。

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

在理论情况下,如果Haskell允许相同的绑定:

因此,允许多个绑定将会有问题。

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没有像那样的语法。不过它确实有guards,正如其他人所提到的。

还要注意:

在Haskell中,在case语句中使用|起到了不同的作用。|后面的语句作为guard。

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

如果这种语法要引入到Haskell中,就必须使用除|之外的其他符号。我建议使用,(向任何想将其添加到Haskell规范中的人提供此建议)。

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

目前这会产生“在 , 上输入解析错误”


7
在OCaml和F#中,哪些变量被绑定了?是 x 和 y 吗?还是 z?它们要求每个模式都要绑定完全相同的绑定。 - newacct
2
@newacct 很棒。很高兴知道我的理论是正确的。我无法想象在Haskell中加入这个功能会很困难,所以我不知道为什么他们还没有做。 - Dan Burton
“罕见”的情况下可以工作的是那些有意义的情况--我们实际上要编写的情况。因此,“罕见”在这里并不是非常减少。然而,我怀疑在实际情况中,它会对类型检查造成问题,因为v必须单态地绑定,在这些情况下,我认为RHS会多态地依赖于v的类型。不过,如果没有分析真实的源代码,我就不确定了。 - luqui
@luqui 你觉得在极少数情况下,使用记录选择器语法来获取你想要的内容是否可行?也就是说,它总是相同构造函数的相同字段对齐,还是有时候是(构造函数一,字段一;构造函数二,字段一),有时候是(构造函数一,字段一;构造函数二,字段二)? - Daniel Wagner

5
这里是一个相当字面的翻译:
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)

并不是说这些选项比其他选项更好,只是在这里提出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接