[英]Stateful integer generation in Haskell
我想編寫一個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,但是我不確定如何設置它。 我對monad相當陌生。
Haskell是純函數,函數不能在相同的參數上生成不同的數據。
如果確實要使用它,則應使用IO a
或ST a
數據,例如IORef a
和STRef a
。
但是您可以通過不同的方式使用純方法:
intGen = [1 .. 10]
intRes = map next intGen
使用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
在第二個中,您的初始值是提供給runStateT
的1
,因此它可以更靈活地從不同的值開始。
綜上所述,通常當您需要延遲生成的整數值時,可以使用列表。 例如,我可能有類似的東西
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))
但在Haskell中,我更喜歡寫類似
import Control.Monad
import System.Directory
import System.FilePath
processFiles :: FilePath -> IO ()
processFiles directory = do
contents <- getDirectoryContents directory
files <- filterM doesFileExist $ map (directory </>) contents
forM_ (zip [1..] files) $ \(i, fname) -> do
let newFName = show i ++ "-" ++ fname
renameFile (directory </> fname) (directory </> newFName)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.