编写"警卫"的首选方法是哪种?

Nom*_*ics 13 haskell

我已经创建了一个函数,我可以使用(据我所知)案例表达或守卫.

foo a b c = case a of 1 -> [...]
                      2 -> [...]
                      3 -> [...] 
                      [...]
                      otherwise -> error "..."
Run Code Online (Sandbox Code Playgroud)

要么

foo a b c | a == 1 = [...]
          | a == 2 = [...]
          | a == 3 = [...] 
          | [...]
          | otherwise = error "..."
Run Code Online (Sandbox Code Playgroud)

所以,问题是:那两个(案件或警卫)中的哪一个是"更好"的编码?两者基本相同吗?

dfl*_*str 13

第一个被认为是更好的风格,原因有两个.

首先:很多人会说它看起来更好,因为你不必输入所有的==.当然,这是一个非常主观的原因.此外,您通常不会引入新的case语句,但只需匹配函数参数列表中的参数,如下所示:

foo 1 b c = ... -- etc
...
foo _ b c = ... -- for the "otherwise" part
Run Code Online (Sandbox Code Playgroud)

这使得代码更加紧凑和可读,这是许多人喜欢的.

其次,实际上存在语义差异.想象一下,你有这样的数据类型:

data Cake = Apple | Cheese | Cream
Run Code Online (Sandbox Code Playgroud)

如果使用第一个方法,则匹配case..of表达式中的构造函数:

case a of
  Apple -> "fruit"
  _     -> "not fruit"
Run Code Online (Sandbox Code Playgroud)

但是,如果你尝试做某种类型的保护表达式,如下所示:

| a == Apple = "fruit"
| otherwise  = "not fruit"
Run Code Online (Sandbox Code Playgroud)

...它实际上不起作用,因为Cake类型没有Eq实例,所以你不能==用来比较两个值.引入Eq实例(deriving (Eq)在数据定义之后)并不总是需要,因此在这种情况下不必执行此操作可能很重要.

  • 请注意,如果在函数定义中使用case语句而不是直接模式匹配,则不同的case可以共享相同的`where`子句,也可以保护. (2认同)

sep*_*p2k 9

当一个警卫可以被重写为其中一个参数的(无保护)案例陈述时,它并不是真正必要的.即你可以把它写成:

foo 1 b c = [...]
foo 2 b c = [...]
foo 3 b c = [...]
[...]
Run Code Online (Sandbox Code Playgroud)

这是写它的首选方式.当您想要的条件不能表示为模式时,您将使用警卫.当你需要匹配除其中一个参数之外的东西时,你会使用case语句.