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

Cur*_*urt 13 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!!"
Run Code Online (Sandbox Code Playgroud)

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

luckyseven' c
    | c == 7 = "SO LUCKY!"
luckyseven' c = "Not so lucky :-/"
Run Code Online (Sandbox Code Playgroud)

谢谢!

bhe*_*ilr 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
Run Code Online (Sandbox Code Playgroud)

仅使用模式匹配就不可能实现这一点.你也做不到这样的事情

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?"
Run Code Online (Sandbox Code Playgroud)

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

data Expr
    = Literal Int
    | Add  Expr Expr
    | Mult Expr Expr
    | Negate Expr
    deriving (Eq, Show)
Run Code Online (Sandbox Code Playgroud)

用守卫写相当于

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)
Run Code Online (Sandbox Code Playgroud)

会更冗长,更难,更烦人.事实上,在某种程度上你必须采用模式匹配来做类似的事情

getLiteral :: Expr -> Int
getLiteral (Literal i) = i
getLiteral _           = error "Not a literal"
Run Code Online (Sandbox Code Playgroud)

其中介绍的功能可能error很糟糕.在这种情况下,使用模式匹配比使用警卫更受欢迎.

  • 请注意,具有更强大功能的语言有时可以通过模式匹配实现更多功能 例如,在Coq中,你可以编写一个`forall x,甚至x\/ odd x`的证明(带有'even`和`odd`的合适归纳定义)然后在应用于你的证明结构上进行模式匹配变量. (2认同)