首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在两个“也许”的单元组中将值相乘?

在两个“也许”的单元组中将值相乘?
EN

Stack Overflow用户
提问于 2014-03-08 10:41:36
回答 3查看 3.9K关注 0票数 6

我目前正在尝试学习Haskell,并且遇到了一个关于Maybe monad的奇怪问题,我似乎无法弄清楚。

作为一项实验,我目前正在尝试取一个字符串,将每个字母转换为任意数字,并将它们相乘/组合在一起。到目前为止,我的情况如下:

代码语言:javascript
运行
复制
lookupTable :: [(Char, Int)]
lookupTable = [('A', 1), ('B', 4), ('C', -6)]

strToInts :: String -> [Maybe Int]
strToInts = map lookupChar
    where 
        lookupChar :: Char -> Maybe Int
        lookupChar c = lookup c lookupTable

-- Currently fails
test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x * y | (x, y) <- zip seq $ tail seq, x < y ]

main :: IO ()
main = do
    putStrLn $ show $ test $ strToInts "ABC"

当我尝试运行它时,它返回以下错误:

代码语言:javascript
运行
复制
test.hs:13:16:
    Could not deduce (Num (Maybe n)) arising from a use of `*'
    from the context (Num n, Ord n)
      bound by the type signature for
                 test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
      at test.hs:12:9-48
    Possible fix: add an instance declaration for (Num (Maybe n))
    In the expression: x * y
    In the expression: [x * y | (x, y) <- zip seq $ tail seq]
    In an equation for `test':
        test seq = [x * y | (x, y) <- zip seq $ tail seq]

我不能百分之百确定为什么会发生这个错误,也不确定它到底意味着什么,尽管我怀疑这可能是因为我试图将两个Maybe monads相乘--如果我将test的定义更改为以下内容,程序就会编译并运行良好:

代码语言:javascript
运行
复制
test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x | (x, y) <- zip seq $ tail seq, x < y ]

我还尝试将类型声明更改为下面的内容,但这也不起作用。

代码语言:javascript
运行
复制
test :: (Num n, Ord n) => [Maybe n] -> [Num (Maybe n)]

我真不知道该怎么纠正这个错误。我对Haskell非常陌生,所以这可能只是我错过了一些非常简单的东西,或者我已经把所有的东西都安排得完全错误了,但这是在阻挠我。我做错了什么?

EN

Stack Overflow用户

发布于 2014-03-08 11:33:15

Reite的答案是正确的,通常是我建议如何处理它--然而,在我看来,您似乎不太了解如何处理Maybe值;如果是这样的话,现在看应用程序函子就没什么意义了。

Maybe的定义基本上是

代码语言:javascript
运行
复制
 data Maybe a = Nothing | Just a

这基本上意味着,在通俗易懂的英语中,它听起来就像“Maybe a类型的值要么是该类型的值Nothing,要么是表单Just a的值”。

现在您可以使用模式匹配来处理这个问题了,下面是列表的例子:

代码语言:javascript
运行
复制
 maybeReverse :: Maybe [a] -> Maybe [a]
 maybeReverse Nothing = Nothing
 maybeReverse (Just xs) = Just $ reverse xs

这基本上意味着“如果值是Nothing,那么没有什么可逆转的,所以结果又是Nothing,如果值是Just xs,那么我们可以用Just重新包装它,将其转化为Maybe [a]值)。

当然,编写像这样的函数,对于我们想要使用的每一个Maybe值来说都是乏味的;因此,更高级的函数来拯救我们吧!这里的观察是,在maybeReverse中,我们没有对reverse做太多事情,我们只是将它应用到包含的值中,并在Just中封装了结果。

因此,我们可以编写一个名为liftToMaybe的函数,为我们这样做:

代码语言:javascript
运行
复制
 liftToMaybe :: (a->b) -> Maybe a -> Maybe b
 liftToMaybe f Nothing = Nothing
 liftToMaybe f (Just a) = Just $ f a

我们还可以进一步观察到,因为函数是值,所以我们也可以有函数的Maybe值。为了做任何有用的事情,我们可以再次打开它们.或者,注意到我们处于与最后一段相同的情况,并立即注意到我们并不真正关心Maybe值中究竟存在什么函数,只需直接编写抽象:

代码语言:javascript
运行
复制
 maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
 maybeApply Nothing _ = Nothing
 maybeApply _ Nothing = Nothing
 maybeApply (Just f) (Just a) = Just $ f a

使用上面的liftToMaybe函数,我们可以简化一点:

代码语言:javascript
运行
复制
 maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
 maybeApply Nothing _ = Nothing
 maybeApply (Just f) x = liftToMaybe f x

Reite的答案中的<$><*>运算符基本上只是liftToMaybe (也称为fmap)和maybeApply的infix名称;它们有以下类型

代码语言:javascript
运行
复制
(<$>) :: Functor f => (a->b) -> f a -> f b
(<*>) :: Applicative f => f (a->b) -> f a -> f b

您现在并不需要知道FunctorApplicative是什么东西(尽管您应该在某个时候研究它们;它们基本上是对其他类型的“上下文”的上述Maybe函数的概括)--基本上,只需用Maybe替换f,您就会发现这些函数基本上都是我们前面讨论过的函数。

现在,我把这个应用到你原来的乘法问题上(尽管其他的答案有点坏了)。

票数 6
EN
查看全部 3 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22268226

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档