Haskell使用Maybe列表

时间:2016-06-23 05:53:46

标签: list haskell maybe

我对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]’

所以我玩了一下,但没有办法访问列表成员。有人可以帮我吗?一点点解释都是件好事!

3 个答案:

答案 0 :(得分:5)

一般来说:

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 [] iddocs for maybe)或maybeToList1 = fromMaybe [],但由于您刚刚开始,您可能希望稍后再回到此处。

答案 1 :(得分:5)

要从不同的方向处理您的问题,我认为您想要一个处理两个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只是捕获&#34;发生了一些不好的事情&#34;,我们可以使用{ {1}}捕获&#34;发生了一些不好的事情,这里是对发生的事情的描述。回顾Either,&#34; playWithMaybe - ness&#34;仅显示在一个位置,最后一行中为Maybe。事实证明,Haskell提供了一个泛型函数Just来包装纯值,就像我们从pure得到的那样,在最小的上下文中:

yourFunction

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

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

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

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

为什么我突然从Monad切换到Applicative? Applicative类似于Monad,但更通用,因此如果可以的话,通常最好使用Applicative(类似于我选择使用import Control.Applicative play' :: Monad m => m [a] -> m [a] -> m [a] play' = liftA2 yourFunction 而不是pure之前)。有关更完整的解释,我强烈建议了解你一个Haskell http://learnyouahaskell.com/chapters),特别是第11章和第12章。注意 - 一定要先阅读第11章! Monad只有掌握了Functor和Applicative后才有意义。

答案 2 :(得分:0)

正如其他人所说,[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 [],以获取列表。