【问题标题】:iteration with the list monad使用列表单子进行迭代
【发布时间】:2022-11-11 12:37:47
【问题描述】:

我无法理解如何从其definition 派生列表单子的迭代行为。

instance Monad [] where
  m >>= f  = concatMap f m
  return x = [x]
  fail s   = []

我读过的讨论似乎忽略了>>= 如何创建控制结构的问题,如do 符号最清楚地显示:

allEvenOdds :: Int -> [(Int,Int)]
allEvenOdds n = do
  evenValue <- [2,4 .. n]               
  oddValue <- [1,3 .. n]                
  return (evenValue,oddValue)

这是内置在 Haskell 中的吗,我假设 IO monad 与实际 i/o 的接口的方式是?

【问题讨论】:

  • do 符号是去糖的,所以它被转换为 [2,4 .. n] &gt;&gt;= \evenValue -&gt; [1, 3 .. n] &gt;&gt;= \oddValue -&gt; return (evenValue, oddValue),并且根据 monad 类型,它会导致不同的语义。因此它不是“硬连线”到IO
  • 简而言之,IO 基本上是State 的半魔法特化。魔法 (IIUC) 的主要来源是,即使 runIO 存在,也没有您可以提供的 Haskell 术语作为初始状态。粗略地说,您定义了一个名为mainIO 操作,运行时会为您执行类似runIO main MagicStateOfTheWorld 的操作。

标签: list haskell monads


【解决方案1】:

没有内置任何东西,一切都是您引用的 Monad 实例的简单结果(并且,由于此示例使用 do 表示法,因此如何取消使用 &gt;&gt;= 运算符):

allEvenOdds n = do
  evenValue <- [2,4 .. n]               
  oddValue <- [1,3 .. n]                
  return (evenValue,oddValue)

-- desugaring the do notation
allEvenOdds n =
  [2,4 .. n] >>= evenValue ->
  [1,3 .. n] >>= oddValue ->
  return (evenValue, oddValue)

-- using the list instance of Monad to replace >>= and return
allEvenOdds n =
  concatMap (evenValue ->
    concatMap (oddValue -> [(evenvalue, oddValue)]) [1,3 .. n]
  ) [2,4 .. n]

希望您可以轻松地看到“迭代”两个列表并生成所有(偶数,奇数)对的列表,其中的值取自两个列表。

在高层次上,我们可以说 list monad 导致迭代只是因为 concatMapmap 一样,为列表的每个元素执行给定的函数,因此它隐式地迭代列表。

【讨论】:

    【解决方案2】:

    Monad 类型类模型的列表实例非决定论:您可以像在 Python 中一样将每个 var &lt;- someList 视为 for 循环。

    do 符号被取消为 [2,4 .. n] &gt;&gt;= (evenValue -&gt; [1, 3 .. n] &gt;&gt;= (oddValue -&gt; return (evenValue, oddValue))),所以这相当于 Python 中的一些东西,例如:

        result = []
        for evenValue in range(2, n, 2):
            for oddValue in range(1, n, 2):
                result.append((evenValue, oddValue))
    

    或列表理解:

        result = [
            (evenValue, oddValue)
            for evenValue in range(2, n, 2)
            for oddValue in range(1, n, 2)
        ]
    

    这适用于instance Monad [],因此表达式[2,4 .. n] &gt;&gt;= (evenValue -&gt; [1, 3 .. n] &gt;&gt;= (oddValue -&gt; return (evenValue, oddValue))) 等价于:

    concatMap (evenValue -> [1, 3 .. n] >>= (oddValue -> return (evenValue, oddValue))) [2,4 .. n]
    

    因此:

    concatMap (evenValue -> concatMap (oddValue -> [(evenValue, oddValue)]) [1, 3 .. n]) [2,4 .. n]
    

    但是do 表示法不是“硬连线”到IOIO 只是Monad 的一个实例,它的实现方式是一个IO 动作将在第二个动作之前运行。因此,对于列表,它的实现方式与跨越 Python for 循环的方式相同。

    【讨论】:

    • 谢谢。我并不是说do 表示法和 IO 之间存在联系,只是实际读取/写入 i/o 的代码没有在 Haskell 中实现(我认为它是在 C 中?)我想列表可能有类似的东西。我没有想到可以通过嵌套concatMaps 来解释这种行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2015-03-07
    相关资源
    最近更新 更多