我有这段代码
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
显然InBox
和InBag
具有相同的结构。我想创建一个包含它们的功能。我不知道如何获取是将构造函数(Box
或Bag
)作为参数传递。
理想情况下,一般功能看起来像这样:
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
因为它无法匹配这样的功能。
我想这样做的原因是因为我有一个包含二进制操作的复杂数据类型(例如+,#,::等等)我有几个函数对于这些构造函数几乎相同。我不想写所有这些并一起修改它们。我必须有办法在函数中完成它。也许有人可以在评论中提出另一种方法吗?
答案 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
。一般来说,Prism
s can form a weak kind of first-class pattern,但在这里使用它们肯定会有点矫枉过正。
答案 1 :(得分:4)
你可能会喜欢first-class-patterns,这是一个让你传递和模仿模式的软件包。
答案 2 :(得分:2)
你想要一个lens
ׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅׅ
答案 3 :(得分:1)
如果希望函数根据参数只计算一种类型,则可以使用普通枚举类型来完成这项工作。所需要的只是一个标签,以区分您想要的操作类型。传递构造函数或类似make_bag
,make_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)
希望这有帮助!