2016-12-19 83 views
1

我有一個小小的互動遊戲程序。交互式部分看起來是這樣的,Haskell中的IO不可見

main :: IO() 
main = playGame newGame 
    where 

    playGame :: Game -> IO() 
    playGame game = 
    do putStr $ show game 
     putStr $ if gameOver game then "Another game? (y or n) > " 
           else show (whoseMove game) ++ " to play (row col) > " 
     moveWords <- fmap (words . fmap cleanChar) getLine 
     if stopGame game moveWords 
     then return() 
     else playGame $ if gameOver game then newGame else makeMove game moveWords 

這工作得很好。它顯示遊戲狀態,詢問下一步的行動,應用於移動的狀態,顯示新的狀態等

然後,我通過Moss Collum中,他表明,使用以下策略交互的game看到了video與用戶。

... 
userInput <- getContents 
foldM_ updateScreen (12, 40) (parseInput userInput) where 
... 

我無法找到foldM_參考,但假設它是某種fold我嘗試這樣做。 (其實,我嘗試了一些東西,但是這似乎是清楚的。)

main' :: IO() 
main' = do 
    moveList <- fmap (map words . lines . map cleanChar) getContents 
    let states = scanl makeMove newGame moveList 
    foldl (\_ state -> putStr . show $ state) (return()) states 

當我運行它,我從來沒有打印出來的比賽狀態,直到擊中檔案結尾之後,此時正確的最終遊戲狀態被打印。在此之前,我可以輸入動作,並且它們被正確處理(根據最終遊戲狀態),但我從來沒有看到中間狀態。 (這個想法是,懶惰的評估應該打印遊戲狀態,因爲它們變得可用。)

我很感激幫助理解爲什麼我沒有看到中間狀態,以及我能做些什麼來修復它。

此外,在輸入文件結尾(Windows上的^ Z)後,程序拒絕再次播放,表示句柄已關閉。要再次播放,我必須重新啓動程序。有沒有辦法解決這個問題?

+3

你摺疊不會產生序列效果(粗略地說,'foldM'中的M)。如果你想使用'foldl',至少應該使用'\ act state - > act >> putStr ....'以便前面的'act'確實運行。我也會嘗試使用'mapM','forM'或他們的應用表親。例如。在導入'Data.Foldable'後試試'for_ [1..10] print' – chi

+0

查看有關hoogle和其他好東西的信息部分的haskell標籤 – jberryman

+0

謝謝。 forM_完成這項工作。現在有沒有辦法在輸入文件結束後重新打開? – RussAbbott

回答

0

首先,讓我們從foldM

foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b 

a是事物在我們的容器類型和b是我們的結果類型。這需要我們在我們的倍, an一個(then ext thing in the list至今積累了b(值,並返回m b,這意味着它返回一個b和做的單子一些單子行動m

然後,它需要一個初始值,它是一個容器a's,它返回一個

這基本上就像一個正常的摺疊函數,但摺疊的每一步都是單點的,最終的結果是單點的,所以如果你使用foldM

現在讓我們來看看foldM_

foldM_ :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m() 

來源

這是相同的,但最終的結果是m()。這是針對我們不關心最終結果的情況。我們保留中間結果並使用它們在每個步驟執行一次性操作,但是當我們完成時,我們只關心我們在monad中做了什麼,因此我們拋棄結果。

在你的情況下,t應該是List,而m應該是IO。因此foldM_遍歷列表,在每個階段執行選定的IO操作,然後拋出最終結果。

fold實際上並未將動作排列在一起,即使您的最終結果爲IO,它也只是正常地疊加在列表中。所以你的foldl創建一個IO動作,即putStr . show $ state,然後傳遞給下一步。但是,你忽略了你的棄牌的第一個參數,所以它會拋出IO而不用做任何事情!

這是Haskell中棘手的問題。類型IO Something的值在創建時並不實際執行操作。它只是創建一個IO值,它只是運行時的一條指令,當它運行main時要執行的操作。如果你把它扔掉,它永遠不會被排序到你的主要執行的IO,那麼副作用將永遠不會發生。