让我们说我们有这种类型的声明:
data D a = A a | B a | C a | D a | E a | F a
并希望在其上定义一个函数,该函数将数据构造函数分为2组。写这样的东西会很好:
g x | x `is` [A,B,C] = 1
| x `is` [D,E,F] = 2
而不是分别在每个构造函数上匹配。
有没有办法实现这个目标?我看着uniplate,却无法找到办法。
答案 0 :(得分:5)
如果您经常需要匹配同一组构造函数,辅助函数可能是最简单的解决方案。例如:
getAbc :: D a -> Maybe a
getAbc (A v) = Just v
getAbc (B v) = Just v
getAbc (C v) = Just v
getAbc _ = Nothing
使用这样的辅助函数,g
的定义可以简化为:
g x = g_ (getAbc x)
where
g_ (Just v) = 1
g_ Nothing = 2
或者,使用maybe
函数:
g = maybe 2 (\v -> 1) . getAbc
答案 1 :(得分:4)
编辑:如果所有构造函数都具有相同类型的字段,则可以滥用Functor:
{-# LANGUAGE DeriveFunctor #-}
data D a = A a | B a | C a | D a | E a | F a
deriving (Eq, Functor)
isCons :: (Eq (f Int), Functor f) => f a -> (Int -> f Int) -> Bool
isCons k s = fmap (const 42) k == s 42
is :: (Eq (f Int), Functor f) => f a -> [Int -> f Int] -> Bool
is k l = any (isCons k) l
g :: D a -> Int
g x | x `is` [A,B,C] = 1
| x `is` [D,E,F] = 2
你可以尝试
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data D a = A a | B a | C a | D a | E a | F a
deriving (Typeable, Data)
g :: Data a => D a -> Int
g x | y `elem` ["A","B","C"] = 1
| y `elem` ["D","E","F"] = 2
where y = showConstr (toConstr x)
答案 2 :(得分:2)
我试图用以下方式概括@KennyTM的答案:
data D a = A a | B a | C a a | D
deriving (Show, Eq, Functor)
class AutoBind a where
bindSome :: forall b . (a -> b) -> b
instance AutoBind Bool where bindSome f = f False
instance Num a => AutoBind a where bindSome f = f 0
class AutoConst a b | a -> b where {- bind until target type -}
bindAll :: a -> b
instance AutoBind a => AutoConst (a -> b) b where bindAll = bindSome
instance (AutoBind a, AutoConst b c) => AutoConst (a -> b) c where bindAll = bindAll . bindSome
isCons :: (Eq (f a), AutoBind a, AutoConst b (f a), Functor f) => f a -> b -> Bool
isCons x y = fmap (bindSome const) x == bindAll y
但由于某种原因,它不适用于构造函数C
答案 3 :(得分:0)
这有点像黑客,但是如何使用Data.Data
和“占位符”类型呢?
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data X = X deriving (Show, Data, Typeable)
data D a = A a | B a | C a | D a a | E a a | F a a
deriving (Show, Data, Typeable)
matchCons :: (Data a) => D a -> [D X] -> Bool
matchCons x ys = any ((== xc) . toConstr) ys
where xc = toConstr x
g :: (Data a) => D a -> Int
g x | matchCons x [A X, B X, C X] = 1
| matchCons x [D X X, E X X, F X X] = 2
请注意,这可以避免类型签名/不同构造函数arity的问题。也许有一种更简洁的方法可以做类似的事情。
答案 4 :(得分:0)
我希望Haskell模式可以指定两种模式的“OR”,类似于OCaml中的|
:
(* ocaml code *)
let g x = match x with
A v | B v | C v -> 1
| C v | D v | E v -> 2
答案 5 :(得分:0)
我有同样的问题。我的解决方案是使用一个视图,虽然我个人喜欢在语义上更符合规范的东西(在我写的一些代码中,懒惰保存是关键的,所以任何额外不需要的模式匹配都会使该技术无法使用)。
{-# LANGUAGE ViewPatterns #-}
data D a = A a | B a | C a | D a | E a | F a
isABC (A v) = Just v
isABC (B v) = Just v
isABC (C v) = Just v
isABC _ = Nothing
f :: D Int -> Int
f (isABC -> Just v) = v
f (isABC -> Nothing) = 0