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

时间:2014-07-28 17:34:23

标签: haskell functional-programming

我正在学习Haskell,并且我不总是清楚何时使用匹配器以及何时使用警卫。对于某些情况,似乎可以使用匹配器和防护装置来实现基本相同的目的。是否有一些规则或启发式方法可以更好地使用匹配防护,反之亦然?比另一个更有效率吗?

为了说明我所得到的,这里有几个我编写的愚蠢的例子似乎是等价的,但是一个版本使用匹配器而另一个使用守卫:

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!!"

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

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

谢谢!

2 个答案:

答案 0 :(得分:17)

这些通常可以互换使用,但两者之间存在显着差异。模式匹配只能在构造函数上进行,因此计算不能在模式内部执行,而防护只是多分支的if-else语句。例如,我无法编写与以下内容等效的模式:

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

仅使用模式匹配是不可能的。你也做不到像

这样的事情
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?"

相反,使用ADT的警卫在没有辅助功能的情况下不会提供太多信息。如果我有类型

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

使用警卫写相等的

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)

会更冗长,更难,更烦人。事实上,在某种程度上,你不得不求助于模式匹配来做像

这样的事情
getLiteral :: Expr -> Int
getLiteral (Literal i) = i
getLiteral _           = error "Not a literal"

其中引入了error的功能,这很糟糕。在这种情况下,使用模式匹配比使用警卫更受欢迎。

答案 1 :(得分:1)

对于您的特定示例,我会使用模式匹配,但会尽可能使用_:

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

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

这强调如果列表不是空的,或者Int不是7,那么其他任何事情都不重要,并且你不会使用它的特定值来产生函数结果。 bheklilr强有力地指出了一个选择或另一个选择绝对可取的地方。