示例:
data A =
A B D
| Aa B C
| Ag B X X
| Ae B R Q
| Ax X
getB a = case a of
(A b _) -> b
(Aa b _) -> b
(Ag b _ _) -> b
(Ae b _ _) -> b
(Ax _) -> somethingElse
在Haskell中,给定一个数据类型,其中许多构造函数具有相同的参数类型,是否有更好的方法来返回此参数。或者是否有更好的方法来编写上面显示的case
语句以减少重复次数?
答案 0 :(得分:4)
ML中提供的名为"或模式"的功能对此有很大帮助。五年前GHC的这个特征是requested,但似乎没有人承担指定细节和实际实施细节的任务。但是,有一个package提供了一种方法来使用Template Haskell做这样的事情,如Or-patterns in Haskell中所述
答案 1 :(得分:3)
您可以使用Prism
和(^?)
operator中的lens
package来简化此操作:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
-- ...
data A =
A { _b :: B, _d :: D }
| Aa { _b :: B, _c :: C }
| Ag { _b :: B, _x1 :: X, _x2 :: X }
| Ae { _b :: B, _r :: R, _q :: Q }
| Ax { _x1 :: X }
makeLenses ''A
getB :: A -> B
getB a = case a ^? b of
Just theB -> theB
Nothing -> somethingElse
对模板Haskell函数makeLenses
的调用负责所有样板。
lens
可能有点依赖,如果您只是为此而使用它,但需要考虑(特别是如果您已经在使用lens
/考虑使用{{1} })。
答案 2 :(得分:3)
你可以使用记录语法来解决这个问题:
data A =
A {fieldB :: B, fieldC :: C} |
Aa {fieldB :: B, fieldX1 :: X, fieldX2 :: X} |
Ag {fieldB :: B, fieldR :: R, fieldQ :: Q} |
Ax {fieldX :: X}
getB a = case a of
Ax -> somethingElse
_ -> fieldB a
关键是要为B
类型的所有字段提供相同的名称。
答案 3 :(得分:2)
函数级别的模式匹配有助于提高可读性,但由于这些都是不同的构造函数,因此无法一次模式匹配多个(我知道)。
getB (A b _) = b
getB (Aa b _) = b
getB (Ag b _ _) = b
getB (Ae b _ _) = b
getB (Ax _) = somethingElse
答案 4 :(得分:1)
如果A
有数据实例,您可以写
import Data.Data
mgetB :: A -> Maybe B
mgetB = gmapQi 0 cast
然后根据该函数定义getB
答案 5 :(得分:1)
在某些时候,你必须说明你的B
是如何包含在A
中的,所以你们很多人一劳永逸地做到这一点。
bOrX a = case a of
(A b _) -> B' b
(Aa b _) -> B' b
(Ag b _ _) -> B' b
(Ae b _ _) -> B' b
(Ax x) -> X' x
随后您可以使用非常少的代码一次匹配所有B
。
getB a = case bOrX a of
B' b -> b
X' _ -> somethingElse
anotherFunctionWithBandX a = case bOrX a of
B' b -> f b
X' x -> g x