跳转到内容

Haskell/理解单子/解决方案/IO

来自维基教科书,开放世界中的开放书籍
[编辑 | 编辑源代码]

map 函数将打印出列表中的每个值,但会生成 [IO ()]。因此我们需要链式 sequence $ map,这正是 mapM 函数的定义方式。因为我们不是收集 print 的结果,而是只关注它的操作,所以我们可以用 mapM_ 来丢弃它们。

printList :: Show a => [a] -> IO ()
printList = mapM_ print

概括兔子入侵示例

[编辑 | 编辑源代码]

如果我们没有使用单子,则将函数 f :: a -> a 应用三次将通过 f(f(f(x))) 实现,这可以改写为 (f . f . f) x。这表明,如果我们想将其推广到将函数应用 n 次,我们可以使用 (.) 作为累积函数来折叠 f 列表

composeTimes :: Int -> (a -> a) -> (a -> a)
composeTimes n f = foldr (.) id $ replicate n f

foldr 的初始值为 id,因为 .

现在我们要做的就是将它翻译成单子,即用单子组合运算符 (>=>) 替换 (.),用 return 替换 id,用 generation 替换任意 f,得到

generations :: Int -> a -> [a]
generations n = foldr (>=>) return $ replicate n generation

我们可以验证它是否按预期工作

Prelude> ["bunny"] >>= generations 0
["bunny"]
Prelude> ["bunny"] >>= generations 1
["bunny","bunny","bunny"]
Prelude> ["bunny"] >>= generations 2
["bunny","bunny","bunny","bunny","bunny","bunny","bunny","bunny","bunny"]

sequenceMaybe 单子中的预期行为是什么?

[编辑 | 编辑源代码]

Maybe 单子表示可能的失败。如果在计算列表中的任何时候发生错误,则整个列表将无法计算,因此我们预期列表的 sequence 将在列表中存在 Nothing 时生成 Nothing,否则生成 Just 列表。这实际上就是发生的情况

Prelude> sequence [Just 1, Just 2, Just 3]
Just [1,2,3]
Prelude> sequence [Just 1, Nothing, Just 3]
Nothing
华夏公益教科书