haskell - 为什么我必须调用 `sum` 两次来对 `Maybe Integer` 的列表求和?

标签 haskell sum option-type

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

Calculate the number of grains of wheat on a chessboard given that the number on each square doubles. Write code that shows how many grains are on a given square, and the total number of grains on the chessboard.

如果输入小于 1 或大于 64,该函数必须返回一个 Maybe 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]))

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

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

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

total = sum [2^n | n <- [0..63]]

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

最佳答案

让我们看一下sum的类型:

sum :: (Foldable t, Num a) => t a -> a

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

sum :: Num a => [a] -> a

如果我们尝试在您的示例中的这种类型上使用 sum,我们将收到类型错误,因为您有一个 Maybe 数字列表,而不是数字列表。相反,您编写 fmap sum [Just 1],将 sumfmap 特化为:

sum :: Maybe Integer -> Integer
fmap :: (Maybe Integer -> Integer) -> [Maybe Integer] -> [Integer]

所以问题实际上不是“为什么 sum 不添加东西”,而是“当给定一个 Maybe 时,sum 如何有一个有意义的定义整数?”

如果您不熟悉如何将 sum 解释为 FoldableMaybe 是如何工作的,这是回答该问题的一种方法可折叠,只是尝试自己实现它。实际上只有一种合理的实现方式:

sum :: Maybe Integer -> Integer
sum Nothing = 0
sum (Just x) = x

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

在此之后,当然很简单:您已经将您的 [Maybe Integer] 变成了 [Integer],当然还有 summing 让您得到非 Nothing 条目的总和。

关于haskell - 为什么我必须调用 `sum` 两次来对 `Maybe Integer` 的列表求和?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68886619/

相关文章:

haskell - 为什么具有多个生成器的 Haskell 列表推导将最右边的生成器视为最紧密的循环?

haskell - 获取 `Include' ` 之外的记录

python - 一行列表中的平方和?

swift - "Optional that can be string"还是 "String that is optional"?

haskell - 过滤掉不需要的元组

sql-server - 递归查询使用初始查询中返回的日期作为后续查询中的限制

php - Mysql一次查询多个SUM - 新手

C# FP : Validation and execution with error handling functional way - space for improvement?

rust - 是否有一种简单的方法来链接返回选项值的函数的结果?

haskell - Strict Maybe 在数据定义中