我想写一个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相当新。
答案 0 :(得分:1)
Haskell是纯粹的,函数不能在相同的参数上生成不同的数据。
如果您确实想要使用它,则应使用IO a
或ST a
数据,例如IORef a
和STRef 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))