一次匹配多个数据类型构造函数

时间:2010-07-18 11:04:14

标签: haskell

让我们说我们有这种类型的声明:

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,却无法找到办法。

6 个答案:

答案 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