我试图在 case 语句中匹配许多不同的构造函数。为了简单起见,假设在一半的情况下我们做同样的事情,而在另一半的情况下我们做其他的事情。即使我将逻辑分解到另一个函数中,我仍然必须写:
case x of
C1 -> foo x
C2 -> foo x
...
C10 -> bar x
C11 -> bar x
...
是否有某种方法可以使 case 语句的行为更像 C 中的
switch
语句(即失败),或者以便我可以一次匹配多种模式之一,例如:
case x of
C1, C2, C3 -> foo x
C10, C11, C12 -> bar x
或者也许有另一种方法来清理这个问题?
这些被称为“析取模式”,Haskell 没有它们。 (OCaml 和 F# 可以。)但是,有一些典型的解决方法。如果您的类型是枚举,则可以使用相等性,例如使用 elem
、使用
case
表达式、守卫或 MultiWayIf
:exampleCase cond = case cond of
c
| c `elem` [C1, C2, C3] -> foo
| c `elem` [C10, C11, C12] -> bar
| otherwise -> baz
exampleGuards c
| c `elem` [C1, C2, C3] = foo
| c `elem` [C10, C11, C12] = bar
| otherwise = baz
exampleIf c
= additionalProcessing $ if
| c `elem` [C1, C2, C3] -> foo
| c `elem` [C10, C11, C12] -> bar
| otherwise -> baz
当然,如果
foo
或
bar
是长表达式,由于懒惰,您可以简单地将它们分解到局部定义中,因此您只需重复名称和需要作为参数的任何模式变量:exampleWhere cond = case cond of
C1 x -> foo x
C2 y -> foo y
…
C10 -> bar
C11 -> bar
…
where
foo x = something long (involving x, presumably)
bar = if you please then something else quite long
如果您经常以这种方式将构造函数分组在一起,则可以使用
PatternSynonyms
语言选项(与
ViewPatterns
结合使用特别有用)来创建自己的模式来匹配此类组:{-# Language
LambdaCase,
PatternSynonyms,
ViewPatterns #-}
-- Write one function to match each property.
fooish :: T -> Maybe X
fooish = \ case
C1 x -> Just x
C2 x -> Just x
…
C10 -> Nothing
C11 -> Nothing
…
-- May use a wildcard ‘_’ here; I prefer not to,
-- to require updating cases when a type changes.
barrish :: T -> Bool
barrish = \ case
C1{} -> False
C2{} -> False
…
C10 -> True
C11 -> True
…
-- Create synonyms for matching those properties.
-- (These happen to be unidirectional only.)
pattern Fooish :: T -> Foo
pattern Fooish x <- (fooish -> Just x)
pattern Barrish :: T -> Bar
pattern Barrish <- (barrish -> True)
-- If they cover all cases, tell the compiler so.
-- This helps produce useful warnings with ‘-Wall’.
{-# Complete Fooish, Barrish #-}
-- Use them just like normal patterns.
exampleSynonyms x = case x of
Fooish x -> …
…
Barrish -> …
…