【问题标题】:Haskell working with Maybe listsHaskell 使用 Maybe 列表
【发布时间】:2016-06-23 05:53:46
【问题描述】:

我对 Haskell 很陌生,不知道如何使用 Maybe [a]。通常我正在编写 OOP (VB.NET) 并且在空闲时间我想学习 haskell(不要问为什么;))。

那么,我想做什么?我想读取两个带有数字 ID 的文件,并仅找出两个文件中匹配的 ID。阅读文件并不是什么大事,它工作起来非常简单。现在,我得到了两个Maybe[Ids] 列表(举个简单的例子,认为ID 是Int)。所以我需要的功能是这样的

playWithMaybe :: (Maybe [a]) -> (Maybe [Int]) -> [Int]

现在我想像以前一样访问列表成员

playWithMaybe (x:xs) (y:ys) = undefined

但不幸的是,GHC 表示这两个列表都不允许这样做

Couldn't match expected type ‘Maybe [Int]’ with actual type ‘[t0]’

所以我玩了一下,但没有找到访问列表成员的方法。有人可以帮我吗?稍微解释一下就好了!

【问题讨论】:

  • Maybe [a] 不是列表类型,它是 Maybe 类型,可能(或可能不)包含列表。 — 我不清楚你想做什么。
  • 我尝试从 Maybe [a] 中提取列表 [a] 以使用列表 [a] 的元素。
  • 就像 list 有两个基本构造函数([]x : xs)一样,Maybe 有两个基本构造函数(NothingJust x),您可以在它们上进行模式匹配。
  • @sandkasten 如果没有列表,你想发生什么?
  • 目前还不是很清楚你为什么拥有 Maybe,以及你打算用 Maybe [a] 做什么(你无能为力)。也许需要更多的上下文。

标签: list haskell maybe


【解决方案1】:

为了从不同的方向解决您的问题,我认为您想要一个处理两个Maybe [a] 的函数。忍受我:

基本上,您要执行的操作对两个列表进行操作,从而为您提供一个新列表,例如,

yourFunction :: [a] -> [a] -> [a]
yourFunction a b = ...

没关系,你可以也应该这样写yourFunction。您拥有的数据是 Maybe [a] 的事实捕获了一些额外的辅助信息:创建输入列表的操作可能已失败。下一步是将yourFunction 与辅助信息链接在一起。这正是do 表示法的目的,将纯操作(如yourFunction)与上下文(您的输入列表之一的创建可能失败):

playWithMaybe :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe maybeA maybeB =
  do a <- maybeA   -- if A is something, get it; otherwise, pass through Nothing
     b <- maybeB   -- if B is something, get it; otherwise, pass through Nothing
     Just (yourFunction a b)  -- both inputs succeeded!  do the operation, and return the result

但事实证明,您可能还想使用其他类型的上下文(一个简单的上下文,而不是仅捕获“发生了不好的事情”的 Maybe,我们可以使用 Either 捕获“发生了不好的事情”发生了,这里是对发生的事情的描述)。回顾playWithMaybe,“Maybe-ness”只出现在一个地方,最后一行的Just。事实证明,Haskell 提供了一个通用的函数 pure 在最小上下文中包装一个纯值,就像我们从 yourFunction 得到的一样:

playWithMaybe' :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe' maybeA maybeB =
  do a <- maybeA
     b <- maybeB
     pure (yourFunction a b)

但是 Haskell 也有一个泛型类型来抽象上下文的概念,即 Monad。这让我们的函数更加通用:

playWithMonad :: Monad m => m [a] -> m [a] -> m [a]
playWithMonad mA mB =
  do a <- mA
     b <- mB
     pure (yourFunction a b)

现在我们有了一些非常通用的东西,事实证明它是如此的通用,它已经在标准库中了! (这变得非常微妙,所以如果还没有意义,请不要担心。)

import Control.Applicative
play :: Monad m => m [a] -> m [a] -> m [a]
play mA mB = liftA2 yourFunction mA mB

甚至

import Control.Applicative
play' :: Monad m => m [a] -> m [a] -> m [a]
play' = liftA2 yourFunction

为什么我突然从 Monad 切换到 Applicative? Applicative 与 Monad 类似,但更通用,因此如果可以选择,通常最好使用 Applicative(类似于我之前选择使用 pure 而不是 return)。要获得更完整的解释,我强烈推荐Learn You a Haskell (http://learnyouahaskell.com/chapters),尤其是第 11 章和第 12 章。注意——一定要先阅读第 11 章!只有掌握了 Functor 和 Applicative 之后,Monads 才有意义。

【讨论】:

  • 我真的很喜欢阅读您的回答,它证明了一个很好的例子和解释。我认为你最后提到的是我的练习将导致的目标:) 也许我在 OOP 中坚持了很多,你总是会检查某事是否什么都不是,这个或那个......
  • @sandkasten 这还会检查列表是成功生成(Just xs)还是(至少)其中一个(Nothing)失败。在 Haskell 中,这一切都是通过使用 liftA2 自动发生的,它使代码非常清晰和简洁 IMO。
【解决方案2】:

一般:

yourFunction Nothing Nothing = ...
yourFunction (Just xs) Nothing = 
  case xs of
    [] -> ...
    x':xs' -> ...
-- or separately: 
yourFunction (Just []) Nothing = ... 
yourFunction (Just (x:xs)) Nothing = ...

等等。哪些情况需要单独处理,取决于具体的功能。您更有可能将Maybe 上的函数与[] 上的函数结合起来。

如果你想为Nothing“只返回一个没有元素的列表”,那么你可以写

maybeToList1 :: Maybe [a] -> [a]
maybeToList1 Nothing = []
maybeToList1 (Just xs) = xs

编写相同函数的更好方法是maybeToList1 = maybe [] id (docs for maybe) 或maybeToList1 = fromMaybe [],但由于您刚刚开始,您可能希望稍后再返回。

【讨论】:

  • 不幸的是,我只能查看一个帖子作为答案,但我也喜欢这个帖子。它与我的 OOP 逻辑保持一致,首先检查某些内容是否包含有效值,然后再开始使用它。
【解决方案3】:

正如其他人所说,[Int]Maybe [Int] 不是一回事。 Maybe [Int] 包含列表可能存在或不存在的额外信息。您说您从文件中读取了 Id。也许,Maybe 表示文件是否存在,而空列表表示文件确实存在但不包含 ID。

如果要使用列表,首先需要定义没有列表时要执行的操作。您可以使用此函数提取列表:

fromMaybe :: a -> Maybe a -> a

也许你想把没有列表当作有一个空列表:

fromMaybe [] :: Maybe [a] -> [a]

也许你想让整个程序崩溃:

fromMaybe (error "Missing list") :: Maybe a -> a

还有更通用的maybe 函数,如果默认值与Maybe 中包含的类型不同,则可以使用该函数:

maybe :: b -> (a -> b) -> Maybe a -> b

这两个函数都在模块Data.Maybe中定义。

您也可以像处理列表一样处理它们,稍后使用Applicative 担心它们的存在。你说你想找到两个列表共有的 Id。你可以这样做:

maybeCommonIds :: Maybe [Int] -> Maybe [Int] -> Maybe [Int]
maybeCommonIds xs ys = intersect <$> xs <*> ys

intersectData.List 中定义。使用maybeCommonIds 将产生Maybe [Int]。包含在里面的列表将保存公共 ID,但如果两个列表中的任何一个都不存在,则没有公共 ID 列表。在您的情况下,这可能与没有通用 ID 相同。在这种情况下,您可能希望将结果传递给fromMaybe [],以取回列表。

【讨论】:

    猜你喜欢
    • 2021-01-03
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 2011-07-17
    相关资源
    最近更新 更多