Haskell:在State中迭代,如何强制我想要的行为?

这是我第一次在SO上发帖,而且我对Haskell比较陌生,所以请原谅任何错误或者我的代码不习惯!

考虑以下两个直观的描述:a,f(a),f(f(a))...

A.一个包含以下内容的列表:a,应用f到a,应用f到那个 ,f应用到那个 ......

B.一个列表,包含在第i个位置,我嵌套f到a的应用程序。

我的问题是,我试图使用Haskell中的iterate函数进行烧毁。我的真实应用程序是一个模拟,但下面的设计示例突出了这个问题。

import Control.Monad.State

example :: State Int [[String]]

step :: [String] -> State Int [String]
step l = do
         currentState <- get
         let result = if (currentState == 1)
                          then "foo":l
                          else "bar":l
         put (currentState + 1)
         return result

example = do
          sequence $ take 3 . iterate (>>= step) $ return []

有了这些定义,

evalState example 1

结果是:

[[],["foo"],["bar","bar"]]

显然, iterate是B,而不是A! 因为step函数只是添加了一些东西到输入列表中,无论状态如何, step ["foo"]都不可能导致["bar", "bar"]

让我说,我明白这里发生了什么,并且 - 在形式上 - 结果恰恰是“应该如此”: step是一个有状态的函数,所以当f(a)作为f (f(a)),它将被重新计算而不是从第二个列表项中取出,因为状态已经改变。 我也意识到我可以通过将我的积累列表放入状态来避免这种现实生活中的应用。

不过,发布此信息有两个原因。

首先,重点是频繁地解释iterate的方式,这可能会误导初学者认为它的确如此,当它实际上是B时。这包括了解你是一个Haskell(我发现它非常有用),但是也会发布SO(例如这里和这里)。 事实上,LYAHFGG中iterate的口头解释几乎完全是上面的定义A. 因此,有一个帖子可能会对此有所帮助,作为其他Haskell新手的资源,因为这些新手因为这个问题而得到一个bug,并且正在寻找一个解释(所以通过所有的方式来发布更准确,技术,更好的措词,澄清A和B之间的差异)。

其次,我仍然会感兴趣的是,是否有一个功能,实际上会做A! 换句话说,在上述有状态的例子中,我怎么能产生这个列表(略有一些符号滥用):[a,b = f(a),f(b),...]? 换句话说,给出

example2 = do
           firstResult <- step []
           secondResult <- step firstResult
           return $ [[], firstResult, secondResult]

为此

evalState example2 1

产生期望的结果

[[],["foo"],["bar","foo"]]

我如何使用iterate重写example2

在初学者Haskell列表上,发布了一个关于iterate的记忆版本的相关问题。 但是,该查询似乎没有收到答案。

我不完全确定懒惰在我的应用程序中是真正的问题。 严格的iterate版本会做我想要的吗? 我自己的,天真的,'严格的迭代'如下所示似乎没有任何区别。

iterate' f x = x : rest
               where previous = f x
                     rest = previous `seq` iterate f previous

任何有关这一切的见解都将非常感谢!


A.和B.之间没有区别,它们通过参照透明度是相同的。
问题的核心似乎是你在执行有状态计算的环境中解释它们。 在这种情况下,你期待的A的类比是
A':产生一个结果列表1.将初始计算的结果放入列表中,2.根据前一个结果确定下一个计算,执行它并将结果附加到列表中,3. goto 2。
B的类比是
B':生成一个计算列表(通过迭代(>> = step)),并通过一个接一个地执行计算得到一个结果列表。
对于无状态计算,或者当您将相同的初始状态传递给B'中生成的所有计算时,唯一的区别在于效率,但如果您使用的是sequence ,则每个计算都以不同的状态开始,因此您从A ”。 打破你的example ,我们有

actionList = take 3 $ iterate (>>= step) (return [])
           = [return [], return [] >>= step, return [] >>= step >>= step]

State Int [String]中的动作(或单值)列表。 现在,当你应用sequence时,

example = sequence actionList

你会得到一个动作,当执行时,这些动作中的第一个以初始状态运行,第二个以第一个状态更新,第三个状态由第二个更新。 为了获得您期望的行为,他们都必须以相同的初始状态运行。

基本上,类型State sv的值是类型s -> (v, s)的函数。 iterate创建一个函数列表, sequence应用这些函数,为它们提供不同的s参数(每个函数获得前一个函数生成的s )。

为了获得理想的行为,我们必须引入一个新的组合器。 非常好,但只有极少数Monads可用

iterateM :: Monad m => (a -> m a) -> m a -> m [a]
iterateM step start = do
    first <- start
    rest <- iterateM step (step first)
    return (first:rest)

哪产生

Prelude Control.Monad Control.Monad.State> evalState (fmap (take 4) (iterateM step (return []))) 1
[[],["foo"],["bar","foo"],["bar","bar","foo"]]

但它只适用于monads中(>>=)Control.Monad.State.Lazy是少数, Control.Monad.State.Strict不是。 即使使用CMSLazy ,你也不能在iterateM之后使用这个状态,你必须先put一个新的状态,然后才能继续计算。 为了使其他monad可用,我们可以添加一个count参数,

iterCountM :: (Monad m) => Int -> (a -> m a) -> m a -> m [a]
iterCountM 0 _ _ = return []
iterCountM k step start = do
    first <- start
    rest <- iterCountM (k-1) step (step fisrt)
    return (first:rest)

所以我们失去了灵活性,但获得更多单子的可用性。


这可能不是回答您提出的问题,但你在做什么听起来极像是unfoldr

step (seed, l) = Just (l', (seed', l'))
  where seed' = succ seed
        l' = (if seed == 1 then "foo" else "bar"):l

ghci> take 3 $ unfoldr step (1, [])
[["foo"], ["bar", "foo"], ["bar", "bar", "foo"]]

没有monads需要。 我在黑暗中刺了一刀,因为你没有详细说明你实际正在做什么,但是我是否得到了正确的step ,外带的信息是, unfoldr也可以用于简单的状态线程。

unfoldr :: (seed -> Maybe (val, seed)) -> seed -> [val]
链接地址: http://www.djcxy.com/p/80795.html

上一篇: Haskell: iterate in State, how to force the behaviour I want?

下一篇: ) and [x: