为什么我必须两次调用“sum”才能对“Maybe Integer”列表求和?

ars*_*anQ 3 haskell sum maybe

我为一个非常简单的练习题写了一个解决方案:

假设每个方块上的数字翻倍,计算棋盘上的小麦粒数。编写代码来显示给定方块上有多少谷物,以及棋盘上的谷物总数。

如果输入小于 1 或大于 64,则函数必须返回 aMaybe Integer并返回Nothing

square :: Integer -> Maybe Integer
square n = if (n < 1 || n > 64) then Nothing else Just (2^(pred n))
total :: Integer
total = sum (fmap sum (map square [1..64]))
Run Code Online (Sandbox Code Playgroud)

我尝试应用fmap sum到GHCI中map square(list of Maybe Integer) 的一些测试输出,并惊讶地发现它返回整数列表 (sans Just) 而不是它们的总和。所以在上面的解决方案中,我sum第二次申请实际得到总和。

我想从概念上理解为什么会这样:换句话说,为什么sum在这种情况下表现得像一个从 Maybe Ints 到 Ints 的转换器,而不是添加东西?

我已经解决了一些依赖辅助函数的类似练习,以避免对Maybe值进行计算的复杂性,也许在这种情况下,我应该只计算 的值total而不使用square,即:

total = sum [2^n | n <- [0..63]]
Run Code Online (Sandbox Code Playgroud)

然而,由于我已经编写了一个有用的函数,我的第一直觉是重用它,这导致了一些不可预见的行为。

ama*_*loy 6

让我们看看类型sum

sum :: (Foldable t, Num a) => t a -> a
Run Code Online (Sandbox Code Playgroud)

通常,对于初学者来说,这是通过假设 简化的t ~ [],因此我们改为使用sumas

sum :: Num a => [a] -> a
Run Code Online (Sandbox Code Playgroud)

如果我们尝试sum在你的例子中使用这种类型,我们会得到一个类型错误,因为你有一个可能的数字列表,而不是一个数字列表。取而代之的是,您编写fmap sum [Just 1]、 专业sumfmap到:

sum :: Maybe Integer -> Integer
fmap :: (Maybe Integer -> Integer) -> [Maybe Integer] -> [Integer]
Run Code Online (Sandbox Code Playgroud)

所以问题不是真正的“为什么不sum添加东西”,而是“sum在给定单个 Maybe Integer 时如何有一个有意义的定义?”

回答这个问题的一种方法是,如果你不熟悉如何解释sum为正在工作Foldable或如何Maybe折叠,那就是尝试自己实现它。实际上只有一种合理的实现:

sum :: Maybe Integer -> Integer
sum Nothing = 0
sum (Just x) = x
Run Code Online (Sandbox Code Playgroud)

对?有人问你“这里的数字总数是多少”,然后给了你零或一个数字。很容易加起来。这正是sumMaybe 的工作方式,除了它通过 Foldable 而不是专门用于 Maybe 。

在这之后,当然很容易:你已经把你的[Maybe Integer]变成了一个[Integer],当然还有summing ,这让你得到了非Nothing条目的总和。


dfe*_*uer 5

让我们看一个例子。

map square [0..2] = [Nothing, Just 1, Just 2]
fmap sum (map square [0..2]) = [sum Nothing, sum (Just 1), sum (Just 2)]
Run Code Online (Sandbox Code Playgroud)

由于Maybe是一个Foldable容器,因此计算其元素的总和是有意义的:

sum Nothing = 0
sum (Just a) = a
Run Code Online (Sandbox Code Playgroud)

所以

fmap sum (map square [0..2]) = [0, 1, 2]
Run Code Online (Sandbox Code Playgroud)

现在我不知道你实际上希望用Maybes做什么,但这就是为什么你得到你所得到的。