Haskell中的有状态整数生成

时间:2014-10-15 13:46:38

标签: haskell generator state monads

我想写一个Haskell函数来生成整数。该函数在第一次调用时返回1,然后在每次调用函数时返回下一个整数。

在Python中,我使用了生成器的概念 - 整数生成器可能看起来像这样:

def intGen():
    x = 1
    while True:
        yield x
        x += 1

integer = intGen()

# Use the generator
next(integer)    # 1
next(integer)    # 2
next(integer)    # 3

如何在Haskell中完成这样的事情?我知道我可能需要State monad,但我不太确定如何设置它;我对monads相当新。

2 个答案:

答案 0 :(得分:1)

Haskell是纯粹的,函数不能在相同的参数上生成不同的数据。

如果您确实想要使用它,则应使用IO aST a数据,例如IORef aSTRef a

但你可以用不同的方式使用纯方法:

intGen = [1 .. 10]

intRes = map next intGen

答案 1 :(得分:0)

使用IORef,您可以执行类似

的操作
import Data.IORef
import Control.Monad ((=<<))

type IntGen = IORef Int

intGen :: IO IntGen
intGen = newIORef 1

next :: IntGen -> IO Int
next gen = do
    val <- readIORef gen
    writeIORef gen $ val + 1
    return val

main :: IO ()
main = do
    integer <- intGen
    -- f =<< m == m >>= f
    print =<< next integer
    print =<< next integer
    print =<< next integer

或者您可以使用State monad完全执行此操作:

import Control.Monad.State

next :: Monad m => StateT Int m Int
next = do
    val <- get
    put $ val + 1
    return val

app :: StateT Int IO ()
app = do
    let stprint = liftIO . print
    stprint =<< next
    stprint =<< next
    stprint =<< next

main :: IO ()
main = void $ runStateT app 1

在第二个中,您的初始值是提供给1的{​​{1}},所以它更灵活,因为您可以从不同的值开始。


所有这一切都说,通常当你需要延迟生成的整数值时,列表是要走的路。例如,我可能有像

这样的东西
runStateT

但在Haskell中,我更喜欢写类似

的内容
def processFile(directory):
    integer = intGen()
    for fname in os.listdir(directory):
        full_fname = os.path.join(directory, fname)
        if os.path.isfile(full_fname):
            i = next(integer)
            new_fname = '{}-{}'.format(i, fname)
            os.rename(full_fname, os.path.join(directory, new_fname))