我对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]’
所以我玩了一下,但没有办法访问列表成员。有人可以帮我吗?一点点解释都是件好事!
答案 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 [] id
(docs 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
intersect
在Data.List
中定义。使用maybeCommonIds
会产生Maybe [Int]
。内部包含的列表将包含公共ID,但如果两个列表中的任何一个不存在,则没有公共ID列表。在您的情况下,这可能与没有常见ID相同。在这种情况下,您可能希望将结果传递给fromMaybe []
,以获取列表。