如何组合 case 语句模式
How to combine case statement patterns
我试图在 case 语句中匹配许多不同的构造函数。为简单起见,假设在一半情况下我们做同样的事情,而在另一半情况下我们做其他事情。即使我把逻辑分解到另一个函数,我仍然要写:
case x of
C1 -> foo x
C2 -> foo x
...
C10 -> bar x
C11 -> bar x
...
有什么方法可以使 case 语句的行为更像 C 中的 switch
语句(即 fallthrough),或者这样我就可以同时匹配多种模式之一,例如:
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 -> …
…
我试图在 case 语句中匹配许多不同的构造函数。为简单起见,假设在一半情况下我们做同样的事情,而在另一半情况下我们做其他事情。即使我把逻辑分解到另一个函数,我仍然要写:
case x of
C1 -> foo x
C2 -> foo x
...
C10 -> bar x
C11 -> bar x
...
有什么方法可以使 case 语句的行为更像 C 中的 switch
语句(即 fallthrough),或者这样我就可以同时匹配多种模式之一,例如:
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 -> …
…