【问题标题】:Filtering for values in Haskell在 Haskell 中过滤值
【发布时间】:2016-06-13 21:47:21
【问题描述】:

我一直在从一本 Haskell 书中做一些 Haskell 练习,其中一项任务是过滤特定类型的值并将它们作为列表返回。

import Data.Time

data Item = DbString String
          | DbNumber Integer
          | DbDate UTCTime
          deriving (Eq, Ord, Show)


database :: [Item]
database =
  [
    DbDate (UTCTime (fromGregorian 1911 5 1) (secondsToDiffTime 34123)),
    DbNumber 9001,
    DbString "Hello World!",
    DbDate (UTCTime (fromGregorian 1921 5 1) (secondsToDiffTime 34123))
  ]

这是我得到的代码,也是我的第一个任务:

Write a function that filters for DbDate values and returns a list of the UTCTime values inside them.函数的模板是:

filterDate :: [Item] -> [UTCTime]
filterDate = undefined

我必须在这里使用的是折叠,因为这是这里关心的问题。

我在 Hoogle 上查找了 Data.Time 模块,但这并没有真正帮助,因为我无法理解如何与该模块进行交互。也许我从错误的角度看待这个问题,因为我认为它与filter 函数无关,而且我认为它与:: 的类型转换无关。

如何获取 UTCTime 值,以及如何过滤它们?

【问题讨论】:

  • 我说“是什么让你认为你应该使用折叠?这个问题并不意味着:我读它的意思是你需要过滤 Item 的列表以返回 DbDate值,然后从每个值中解压缩 UTCTime。” ...但我想我现在看到了曙光。您可以通过遍历导入列表并将有效值连接到输出列表来将过滤器实现为折叠。
  • :: 不是类型转换。
  • 这本书介绍列表推导式了吗?
  • @melpomene 这本书向我介绍了列出理解。我应该使用它们来抽取数据库中的 UTCTime 值吗?
  • 是的,列表推导式可能是解决这个问题的最直接的方法。顺便说一句,您不需要与 Data.Time 进行此任务交互。

标签: haskell


【解决方案1】:

好的,我的 Haskell-fu 非常虚弱,但我会尝试回答。您正在寻找定义遍历列表并对其进行过滤的函数。如果值为DbDate,则返回<that value> : <output list>,否则返回<output list>。通过折叠输入,您可以生成过滤后的输出。 How would you define map and filter using foldr in Haskell? 有一个相关问题可能会更好地解释这一点。

这可以分解为:

filterFn :: Item -> [UTCTime] -> [UTCTime]
filterFn (DbDate x) xs = x:xs
filterFn _ xs = xs

(这可能是语法错误)。这个函数从我们的[Item] 和模式匹配中取出一个项目。

  • 如果它匹配DbDate x,那么x 就是UTCTime,我们将它附加到我们的输入列表中。
  • 如果不是,我们将忽略它并原样返回输入列表。

然后我们可以折叠:

filterDate = foldr filterFn []

这会让你得到答案吗?

【讨论】:

  • 不完全是因为 DbDate 需要一个参数才能用于模式匹配。编译器抱怨 DbDate 缺少参数。
  • 试试filterFn (DbDate x) xs = x:xsfilterFn _ xs = xs。这里只能使用两个参数,所以需要括号。
  • @chi 我认为这更符合编译器同意的内容。我修改了过滤器函数,然后我使用了foldr filterDate [] database,它将时间值作为列表返回。我如何“获取”正确的类型更是一个问题。我唯一担心的是这个函数实际上没有相同的类型签名。据我所知,filterDate 的类型签名是Item -> [UTCTime] -> [UTCTime]
  • @Poriferous 你确定吗?使用您对Item 的定义以及我对上面filterFnfilterDate 的定义,ghci 给了我以下类型签名:filterDate :: [Item] -> [UTCTime],这就是我们想要的。
  • 作为一名 C++ 程序员,我很自然地将 filterFn 函数重命名为 getter,这有助于我更好地理解事物。多亏了你们两个,我才能无缝地解决所有任务。 :-)
【解决方案2】:

Item 被定义为联合类型,这意味着它可以是DbStringDbNumberDbDate

data Item = DbString String
      | DbNumber Integer
      | DbDate UTCTime
      deriving (Eq, Ord, Show)

您可以使用pattern matching 仅获取您感兴趣的值。您需要匹配一个项目,检查它是否是DbDate,如果是这样,请提取它持有的UTCTime 实例。

你说你想使用fold,所以你需要一个累加器,你可以在其中放置你想要保留的值和一个函数来填充它。

filterDate items = foldl accumulate [] items
    where extractTime item = case item of DbDate time -> [time]
                                          _ -> []
          accumulate item accumulator = accumulator ++ (extractTime item)

在上面的代码中,extractTime 模式匹配一​​个项目并返回一个包含时间的列表或返回一个空列表。 accumulate 函数只是将您从前面的步骤中获得的值(它们存储在 accumulator 中)和您将 extractTime 应用于当前项目的值放在一起。

【讨论】:

    猜你喜欢
    • 2014-12-17
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 2020-07-29
    相关资源
    最近更新 更多