簡體   English   中英

迭代IO動作和懶惰

[英]iterate IO actions and laziness

我想編寫《 Hangman》游戲https://github.com/fokot/reactive-hangman/blob/master/src/Hangman.hs,並把用戶操作列表看作是流。 我的遞歸版本可以正常運行(在代碼runGameRecursively中(newGameState“ secret”))

我陷入懶惰問題

updateGameState :: GameState -> IO GameState
updateGameState gs = do
   l <- getALetter gs
   return $ updateState gs l

ff :: (a -> Bool) -> [IO a] -> IO a
ff f (i:is) = do
  res <- i
  if f res then return res else ff f is

runGameInfinite :: GameState -> IO ()
runGameInfinite gs =
  -- infinite lazy game loop
  let repl = tail $ iterate (\x -> x >>= updateGameState) (return gs) :: [IO GameState]
  in do
    endState <- ff gameEnded repl
    putStrLn $ showState endState

main = runGameInfinite (newGameState "car")

當您運行游戲時,代表游戲的每一步都需要重新評估所有先前的內容,即使它們已經存在。 我嘗試玩$! 但尚未找到正確的答案。 謝謝

我認為使用iterate方法制作表面上純粹的IO操作列表的方案是這里麻煩的根源。 您的計划是通過用戶輸入來更新狀態,但是將狀態的繼承視為可以“像列表一樣處理”的流。 如果我使用真正的iterateM來生成正確的流事物,那么事物的運行將完全按照您希望它們進行的方式進行。 所以如果我添加進口

import Streaming -- cabal install streaming
import qualified Streaming.Prelude as S

在您的主要定義之后寫類似

runGameInfiniteStream gs =  S.print $ S.take 1 $ S.dropWhile (not . gameEnded) steps
  where
  steps :: Stream (Of GameState) IO ()
  steps = S.iterateM updateGameState (return gs)

main :: IO ()
main = runGameInfiniteStream (newGameState "car")

然后我得到

>>> main
You have 5 lifes. The word is "___"
Guess a letter: 
c
You have 5 lifes. The word is "c__"
Guess a letter: 
a
You have 5 lifes. The word is "ca_"
Guess a letter: 
r
GameState {secretWord = "car", lives = 5, guesses = "rac"}

我認為這正是您想要的程序,但是使用適當的流概念,而不是以某種復雜的方式混合IO和列表。 pipesconduit和類似的包裝也可以做類似的事情。


(稍后添加:)

要流式傳輸到與純字符列表相對應的狀態(模擬來自用戶輸入的結果),您只需使用scan

pureSteps
   :: (Monad m) => GameState -> [Char] -> Stream (Of GameState) m ()
pureSteps gs chars = S.scan updateState gs id (S.each chars)

這基本上與Prelude.scanl相同,也可以用於(純粹的情況下)查看更新:

>>> S.print $ pureSteps (newGameState "hi") "hxi"
GameState {secretWord = "hi", lives = 5, guesses = ""}
GameState {secretWord = "hi", lives = 5, guesses = "h"}
GameState {secretWord = "hi", lives = 4, guesses = "h"}
GameState {secretWord = "hi", lives = 4, guesses = "ih"}

>>> mapM_ print $ scanl updateState (newGameState "hi") "hxi"
GameState {secretWord = "hi", lives = 5, guesses = ""}
GameState {secretWord = "hi", lives = 5, guesses = "h"}
GameState {secretWord = "hi", lives = 4, guesses = "h"}
GameState {secretWord = "hi", lives = 4, guesses = "ih"}

要查看最終的“獲勝”狀態(如果存在),可以編寫例如

runPureInfinite
  :: Monad m => GameState -> [Char] -> m (Of [GameState] ())
runPureInfinite gs = S.toList . S.take 1 . S.dropWhile (not . gameEnded) . pureSteps gs

-- >>> S.print $ runPureInfinite (newGameState "car") "caxyzr"
-- [GameState {secretWord = "car", lives = 2, guesses = "rac"}] :> ()

等等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM