在Haskell中如何将构造函数作为参数传递并匹配它

时间:2014-02-11 22:15:45

标签: haskell constructor

我有这段代码

data Container = Box Int | Bag Int


inBox :: [Container] -> Int
inBox [] = 0
inBox (x:ls) | (Box i) <- x = i + inBox ls
             | otherwise = inBox ls


inBag :: [Container] -> Int
inBag [] = 0
inBag (x:ls) | (Bag i) <- x = i + inBag ls
             | otherwise = inBag ls

显然InBoxInBag具有相同的结构。我想创建一个包含它们的功能。我不知道如何获取是将构造函数(BoxBag)作为参数传递。

理想情况下,一般功能看起来像这样:

inSome :: Constructor -> [Container] -> Int
inSome con [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome con ls
                  | otherwise = inSome con ls

显然这不起作用,因为构造函数不是这里定义的类型。我该怎么办?

一个想法是将其作为一个函数传递:

inSome :: (Int -> Container) -> [Container] -> Int
inSome _ [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome ls
                  | otherwise = inSome ls

但后来我收到了错误:

  

模式中的解析错误:con

因为它无法匹配这样的功能。

我想这样做的原因是因为我有一个包含二进制操作的复杂数据类型(例如+,#,::等等)我有几个函数对于这些构造函数几乎相同。我不想写所有这些并一起修改它们。我必须有办法在函数中完成它。也许有人可以在评论中提出另一种方法吗?

5 个答案:

答案 0 :(得分:8)

您可以完全避免使用模式匹配。

data Container = Box Int | Bag Int

unBox, unBag :: Container -> Maybe Int

unBox (Box i) = Just i
unBox _       = Nothing

unBag (Bag i) = Just i
unBag _       = Nothing

这些函数的类型捕获了在启用Int的结构时获取包含的Container的需要。然后可以使用它来构建您想要的功能。

inSome :: (Container -> Maybe Int) -> [Container] -> Int
inSome get []     = 0
inSome get (x:ls) = fromMaybe 0 (get x) + inSome ls

inBag = inSome unBag
inBox = inSome unBox

正如leftroundabout所指出的那样,“获得或失败”的模式在Lens的概念中被(大规模地)概括,或者在这种情况下是Prism。一般来说,Prisms can form a weak kind of first-class pattern,但在这里使用它们肯定会有点矫枉过正。

答案 1 :(得分:4)

你可能会喜欢first-class-patterns,这是一个让你传递和模仿模式的软件包。

答案 2 :(得分:2)

你想要一个lensׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅ

答案 3 :(得分:1)

如果希望函数根据参数只计算一种类型,则可以使用普通枚举类型来完成这项工作。所需要的只是一个标签,以区分您想要的操作类型。传递构造函数或类似make_bagmake_box的包装器并没有做得更好,并且它们不能在模式匹配中使用。

data ConType = ON_BAG | ON_BOX
inCon :: ConType -> [Container] -> Int
inCon _ [] = 0 
inCon t (x:ls) | ON_BAG <-t,(Bag i) <- x = i + (inCon t ls)
               | ON_BOX <-t,(Box i) <- x = i + (inCon t ls)
               | otherwise = inCon t ls

如果你想分别将盒子和袋子中的数字相加,我认为你可以这样做:

inCont :: [Container] -> (Int,Int)
inCont [] = (0,0)
inCont (x:ls) | (Bag i) <- x = addP (i,0) (inCont ls)
              | (Box i) <- x = addP (0,i) (inCont ls)
              | otherwise = inCont ls

将它们统计在一起更加容易:

inCont2 :: [Container] -> Int
inCont2 [] = 0
inCont2 (x:ls) | (Bag i) <- x = i + (inCont2 ls)
              | (Box i) <- x = i + (inCont2 ls)
              | otherwise = inCont2 ls

答案 4 :(得分:1)

将容器拆分为两种数据类型:

data Sort = Bag | Box deriving (Eq, Show)
data Container = Container Sort Int deriving (Show)

定义Sort允许我们讨论各种容器而不实际引用任何特定容器。这让我们可以做更多有趣的事情:

import Data.Maybe (fromMaybe)

--  Pull out the value regardless of sort
getVal :: Container -> Int
getVal (Container _ val) = val

--  If the sort passes a predicate, we get just the value
unwrapIf :: (Sort -> Bool) -> Container -> Maybe Int
unwrapIf p (Container b v) = if p b then Just v else Nothing

--  If a given sort matches the container, we get just the value
unwrap :: Sort -> Container -> Maybe Int
unwrap p = unwrapIf (p ==)

--  Unwrap with a default value if one was not found
unwrapDefault :: Int -> Sort -> Container -> Int
unwrapDefault def p = fromMaybe def . unwrap p

--  Unwrap with a default value of 0
unwrapValue :: Sort -> Container -> Int
unwrapValue = unwrapDefault 0

--  Unwrap Bag values, else 0
unbag :: Container -> Int
unbag = unwrapValue Bag

--  Unwrap Box values, else 0
unbox :: Container -> Int
unbox = unwrapValue Box

--  sum up the values in a list of containers
sumAll :: [Container] -> Int
sumAll = sum . map getVal

--  sum up the values in a list of one sort of container
sumContainer :: Sort -> [Container] -> Int
sumContainer s = sum . map (unwrapValue s)

--  sum up the bag values in a list of containers
sumBag :: [Container] -> Int
sumBag = sumContainer Bag

--  sum up the box values in a list of containers
sumBox :: [Container] -> Int
sumBox = sumContainer Box

此处sumContainer相当于您理想的inSome功能,sumBag / sumBox分别为inBag / inBox

如果您想更进一步,请尝试推广Container以允许任何值:

data Container a = Container Sort a deriving (Show)

希望这有帮助!