简体   繁体   English

在Haskell中,守卫或匹配者更受欢迎吗?

[英]In Haskell, are guards or matchers preferable?

I'm learning Haskell, and it's not always clear to me when to use a matcher and when to use a guard. 我正在学习Haskell,我不总是清楚何时使用匹配器以及何时使用防护装置。 For certain scenarios it seems that matchers and guards can be used to achieve essentially the same ends. 对于某些情况,似乎可以使用匹配器和防护装置来实现基本相同的目的。 Are there some rules or heuristics for when it's better to use matches over guards or vice versa? 是否有一些规则或启发式方法可以更好地使用匹配防护,反之亦然? Is one more performant than the other? 比另一个更有效率吗?

To illustrate what I'm getting at, here are a couple of silly examples I cooked up that seem to be equivalent, but one version uses matchers and the other uses guards: 为了说明我所得到的内容,这里有一些我认为相当的愚蠢的例子,但是一个版本使用匹配器而另一个版本使用守卫:

listcheck :: [a] -> String
listcheck [] = "List is null :-("
listcheck a = "List is NOT null!!"

listcheck' a
    | null a = "List is null :-("
    | otherwise = "List is NOT null!!"

and

luckyseven :: Int -> String
luckyseven 7 = "SO LUCKY!"
luckyseven b = "Not so lucky :-/"

luckyseven' c
    | c == 7 = "SO LUCKY!"
luckyseven' c = "Not so lucky :-/"

Thanks! 谢谢!

These can often be used interchangeably, but there are significant differences between the two. 这些通常可以互换使用,但两者之间存在显着差异。 Pattern matching can only occur on constructors, so computations can not be performed inside of a pattern, while guards are simply multi-branch if-else statements. 模式匹配只能在构造函数上进行,因此计算不能在模式内部执行,而防护只是多分支的if-else语句。 For example, I can't write a pattern equivalent of the following: 例如,我无法编写与以下内容等效的模式:

func :: Int -> Int
func x
    | even x = 3 * x
    | odd x  = 7 * x        -- alternatively "otherwise = 7 * x" to get rid of all those pesky compiler warnings

This just wouldn't be possible with just pattern matching. 仅使用模式匹配就不可能实现这一点。 You also can't do things like 你也做不到这样的事情

func :: Int -> Maybe String
func x
    | x < 0     = Nothing
    | x == 0    = Just "Zero"
    | x < 20    = Just "Small"
    | x < 100   = Just "Big"
    | x < 1000  = Just "Huge"
    | otherwise = Just "How did you count that high?"

Conversely, guards using ADTs don't give you much information without helper functions. 相反,使用ADT的警卫在没有辅助功能的情况下不会提供太多信息。 If I had the type 如果我有这种类型

data Expr
    = Literal Int
    | Add  Expr Expr
    | Mult Expr Expr
    | Negate Expr
    deriving (Eq, Show)

Using guards to write the equivalent of 用守卫写相当于

eval :: Expr -> Int
eval (Literal i)  = i
eval (Add  e1 e2) = eval e1 + eval e2
eval (Mult e1 e2) = eval e1 * eval e2
eval (Negate e)   = negate (eval e)

would be a lot more verbose, difficult, and annoying. 会更冗长,更难,更烦人。 In fact, at some level you'd have to resort to pattern matching to do things like 事实上,在某种程度上你必须采用模式匹配来做类似的事情

getLiteral :: Expr -> Int
getLiteral (Literal i) = i
getLiteral _           = error "Not a literal"

Which introduces functions that can error , which is bad. 这引入了可能error功能,这很糟糕。 In this case, using pattern matching is much preferred over using guards. 在这种情况下,使用模式匹配比使用警卫更受欢迎。

For your particular examples, I'd go with pattern matching, but would use _ where possible: 对于您的特定示例,我将使用模式匹配,但在可能的情况下使用_:

listCheck :: [a] -> String
listCheck [] = "List is null :-("
listCheck _  = "List is NOT null!!"

and

luckySeven :: Int -> String
luckySeven 7 = "SO LUCKY!"
luckySeven _ = "Not so lucky :-/"

That emphasizes that if the list isn't empty, or the Int isn't 7, nothing else matters, and you aren't going to use its particular value to produce the function result. 这强调如果列表不为空,或者Int不是7,则其他任何事情都不重要,并且您不会使用其特定值来生成函数结果。 bheklilr has capably pointed out places where one choice or the other is definitely preferable. bheklilr强有力地指出了一个选择或另一个选择绝对可取的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM