代码之家  ›  专栏  ›  技术社区  ›  Daniel

同时匹配多个数据类型构造函数

  •  9
  • Daniel  · 技术社区  · 14 年前

    假设我们有这个类型声明:

    data D a = A a | B a | C a | D a | E a | F a
    

    g x | x `is` [A,B,C] = 1
        | x `is` [D,E,F] = 2
    

    而不是分别在每个构造函数上进行匹配。

    6 回复  |  直到 14 年前
        1
  •  5
  •   sth ACP    14 年前

    如果您经常需要匹配同一组构造函数,那么helper函数可能是最简单的解决方案。例如:

    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
    
        2
  •  4
  •   kennytm    14 年前

    编辑:如果所有构造函数都具有相同类型的字段,则可能会滥用函子:

    {-# 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)
    
        3
  •  2
  •   ony    14 年前

    我试着把“肯尼特”的答案概括为:

    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

        4
  •  0
  •   C. A. McCann Ravikant Cherukuri    14 年前

    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
    

    注意,这避免了类型签名/不同构造函数的问题。或许还有一种更清洁的方法来做类似的事情。

        5
  •  0
  •   newacct    14 年前

    我希望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
    
        6
  •  0
  •   gatoatigrado    13 年前

    我也有同样的问题。我的解决方案是使用一个视图,尽管我个人希望使用更规范的语义等价的视图(在我正在编写的一些代码中,惰性保存非常关键,因此任何额外的不需要的模式匹配都可能使该技术不可用)。

    {-# 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