【问题标题】:Combining State and List Monads结合状态和列表单子
【发布时间】:2014-04-06 09:19:19
【问题描述】:

考虑以下 Haskell 代码:

import Control.Monad.State

test :: Int -> [(Int, Int)]
test = runStateT $ do
    a <- lift [1..10]
    modify (+a)
    return a

main = print . test $ 10

这会产生以下输出:

[(1,11),(2,12),(3,13),(4,14),(5,15),(6,16),(7,17),(8,18),(9,19),(10,20)]

但是我想生成以下输出:

[(1,11),(2,13),(3,16),(4,20),(5,25),(6,31),(7,38),(8,46),(9,55),(10,65)]

这在 JavaScript 这样不纯的语言中很容易做到:

function test(state) {
    var result = [];

    for (var a = 1; a <= 10; a++) {
        result.push([a, state += a]);
    }

    return result;
}

你如何在 Haskell 中做同样的事情?

【问题讨论】:

    标签: haskell monads monad-transformers state-monad non-deterministic


    【解决方案1】:

    Haskell 类型和您的 JavaScript 代码的逻辑不匹配:JS 代码在状态中有两个值(Int 和返回的列表)。相比之下,StateT Int [] a 并没有真正的状态列表;相反,它多次运行有状态操作(每次运行的初始状态不变)并将所有结果收集到一个列表中。

    换句话说,JS 代码的类型为State (Int, [(Int, Int)]) [(Int, Int)]。但是这个翻译太直白了,我们可以写出更优雅的 Haskell 代码。

    坚持State monad,我们可以返回一个带有mapMforM 的列表:

    test2 :: Int -> [(Int, Int)]
    test2 = evalState $ 
        forM [1..10] $ \a -> do
            s <- get <* modify (+a)
            return (a, s) 
    

    一些lens的魔法可以让它更类似于JS代码:

    {-# LANGUAGE TupleSections #-}
    
    import Control.Lens
    
    test3 :: Int -> [(Int, Int)]
    test3 = evalState $ 
        forM [1..10] $ \a -> (a,) <$> (id <+= a)
    

    但是,我们可以完全取消 State,我认为这是最好的方法:

    import Control.Monad (ap)
    
    test4 :: Int -> [(Int, Int)]
    test4 n = ap zip (tail . scanl (+) n) [1..10]
    -- or without ap : zip [1..10] (drop 1 $ scanl (+) n [1..10])
    

    【讨论】:

    • 函数forM 正是我对State 操作进行排序所需要的。谢谢。
    • 您可以避免使用 TupleSections 和 lambda :\a -&gt; (a,) 正好是 (,)
    【解决方案2】:

    您的系列是二次的,可以更简单地生成:

    series = fmap (\x -> (x, quot (x*x + x) 2 + 10)) [1..]
    

    如果你想要一个递归关系,你可以这样写:

    series :: [(Int,Int)]
    series = let
      series' x y = (x, x + y) : series' (x + 1) (x + y)
      in series' 1 10
    

    我认为没有令人信服的理由使用 monad。

    【讨论】:

      猜你喜欢
      • 2012-06-19
      • 2010-11-23
      • 1970-01-01
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多