使用数据构造函数作为函数参数

企鹅

我正在学习“Haskell 编程……”,并且在第 10 章中,一直在使用一个玩具数据库。数据库定义为:

data DatabaseItem = DBString String
                  | DBNumber Integer
                  | DBDate   UTCTime
                  deriving (Eq, Ord, Show)

并且,给定一个 [databaseItem] 形式的数据库,我被要求编写一个函数

dbNumberFilter :: [DatabaseItem] -> [Integer]

它接受一个 DatabaseItems 列表,根据 DBNumbers 过滤它们,并返回一个存储在其中的 Integer 值列表。

我用以下方法解决了这个问题:

dbNumberFilter db = foldr selectDBNumber [] db
  where
    selectDBNumber (DBNumber a) b = a : b
    selectDBNumber _ b          = b 

显然,我可以编写一个几乎相同的方法来提取字符串或 UTCTTimes,但我想知道是否有办法创建一个通用过滤器,通过将过滤器传递给选定的数据构造函数来提取整数、字符串列表。就像是:

dbGenericFilter :: (a -> DataBaseItem) -> [DatabaseItem] -> [a]
dbGenericFilter DBICon db = foldr selectDBDate [] db
  where
    selectDBDate (DBICon a) b = a : b
    selectDBDate _ b          = b 

其中通过在 DBICon 参数中传递 DBString、DBNumber 或 DBDate,将分别返回字符串、整数或 UTCTimes 的列表。

我无法使上述内容或我能想到的任何变体起作用。但是有没有办法达到这种效果呢?

合金

您不能编写一个如此通用的函数,以至于它只需要一个构造函数作为它的第一个参数,然后执行您想要的操作。模式匹配在 Haskell 中不是一流的——你不能将它们作为参数传递。但是你可以做一些事情来更简单地写这个。

一种实际上不是更通用但肯定更短的方法是利用列表理解中失败的模式匹配跳过该项目的事实:

dbNumberFilter db = [n | DBNumber n <- db]

如果您更喜欢编写一些通用的东西,例如dbNUmberFilter = genericFilter x对于 some x,您可以将“尝试匹配 a DBNumber的概念提取到一个函数中:

import Data.Maybe (mapMaybe)

genericFilter :: (DatabaseItem -> Maybe a) -> [DatabaseItem] -> [a]
genericFilter = mapMaybe

dbNumberFilter = genericFilter getNumber
  where getNumber (DBNumber n) = Just n
        getNumber _ = Nothing

您可以做的另一件有些相关的通用事情是为您的类型定义 catamorphism,这是一种将您的类型的所有可能模式匹配抽象为单个函数的方法:

dbCata :: (String -> a) 
       -> (Integer -> a) 
       -> (UTCTime -> a) 
       -> DatabaseItem -> a
dbCata s i t (DBString x) = s x
dbCata s i t (DBNumber x) = i x
dbCata s i t (DBDate x) = t x

然后您可以dbNumberFilter使用三个函数参数而不是模式匹配来编写

dbNumberFilter :: [DatabaseItem] -> [Integer]
dbNumberFilter = (>>= dbCata mempty pure mempty)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章