Haskell 使用 Maybe 列表
Haskell working with Maybe lists
我是 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]’
所以我试了一下,但没有找到访问列表成员的方法。有人可以帮我吗?稍微解释一下就好了!
总的来说:
yourFunction Nothing Nothing = ...
yourFunction (Just xs) Nothing =
case xs of
[] -> ...
x':xs' -> ...
-- or separately:
yourFunction (Just []) Nothing = ...
yourFunction (Just (x:xs)) Nothing = ...
等等。哪些情况需要单独处理,要看具体的函数。您更有可能将处理 Maybe
的函数与处理 []
.
的函数结合起来
如果你想"Just return an list with no elements"换成Nothing
,那么你可以写
maybeToList1 :: Maybe [a] -> [a]
maybeToList1 Nothing = []
maybeToList1 (Just xs) = xs
编写相同函数的更好方法是 maybeToList1 = maybe [] id
(docs for maybe
) 或 maybeToList1 = fromMaybe []
,但由于您刚刚开始,您可能希望稍后再回到这一点。
为了从不同的方向解决您的问题,我认为您不 想要一个处理两个 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
但事实证明,您可能还想使用其他类型的上下文(一个简单的上下文,而不是仅捕获 "something bad happened" 的 Maybe
,我们可以使用 Either
来捕获 "something bad happened, and here is a description of what happened). Looking back at playWithMaybe
, the "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 之后,Monad 才有意义。
正如其他人所说,[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 []
,以取回列表。
我是 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]’
所以我试了一下,但没有找到访问列表成员的方法。有人可以帮我吗?稍微解释一下就好了!
总的来说:
yourFunction Nothing Nothing = ...
yourFunction (Just xs) Nothing =
case xs of
[] -> ...
x':xs' -> ...
-- or separately:
yourFunction (Just []) Nothing = ...
yourFunction (Just (x:xs)) Nothing = ...
等等。哪些情况需要单独处理,要看具体的函数。您更有可能将处理 Maybe
的函数与处理 []
.
如果你想"Just return an list with no elements"换成Nothing
,那么你可以写
maybeToList1 :: Maybe [a] -> [a]
maybeToList1 Nothing = []
maybeToList1 (Just xs) = xs
编写相同函数的更好方法是 maybeToList1 = maybe [] id
(docs for maybe
) 或 maybeToList1 = fromMaybe []
,但由于您刚刚开始,您可能希望稍后再回到这一点。
为了从不同的方向解决您的问题,我认为您不 想要一个处理两个 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
但事实证明,您可能还想使用其他类型的上下文(一个简单的上下文,而不是仅捕获 "something bad happened" 的 Maybe
,我们可以使用 Either
来捕获 "something bad happened, and here is a description of what happened). Looking back at playWithMaybe
, the "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 之后,Monad 才有意义。
正如其他人所说,[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 []
,以取回列表。