2017-03-17 39 views
0

这是学习StateT monad的练习。该程序实现了游戏莫拉。这两名球员是电脑和一个人。国家累计电脑和玩家的分数。该程序适用于morra函数的一个迭代。然而,我不知道如何循环它。我尝试了一些东西,但似乎没有任何工作。与StateT monad环路

module Morra where 

import Control.Monad.Trans.State.Lazy 
import Control.Monad.IO.Class 
import Data.Char (isDigit, digitToInt) 
import System.Random (randomRIO) 
import Control.Monad (when) 

morra :: StateT (Int, Int) IO() 
morra = do 
    p <- liftIO getChar 
    when (isDigit p) $ 
    do 
     let p' = digitToInt p 
     c <- liftIO $ randomRIO (1, 2) 
     liftIO $ putStrLn ['P',':',' ',p] --"P: " ++ p) 
     liftIO $ putStrLn ("C: " ++ show c) 
     (pt, ct) <- get 
     if even (c + p') then 
     do 
      liftIO $ putStrLn "Computer Wins" 
      put (pt, ct + 1) 
     else 
     do 
      liftIO $ putStrLn "Player Wins" 
      put (pt + 1, ct) 

main :: IO() 
main = do 
    putStrLn "-- p is Player" 
    putStrLn "-- c is Computer" 
    putStrLn "-- Player is odds, Computer is evens." 
    fScore <- runStateT morra (0,0) 
    let personS = fst . snd $ fScore 
     compS = snd . snd $ fScore 
    putStrLn ("Person Score: " ++ show personS) 
    putStrLn ("Computer Score: " ++ show compS) 
    if personS > compS then 
    putStrLn "Winner is Person" 
    else 
    putStrLn "Winner is Computer" 

回答

2

你99%了。只需在最后一个putStrLn之后的新行上添加mainmain即可自行调用,有效地重新启动程序。

一些技巧来简化一些东西在你的代码:

  • 使用execStateT:: StateT s m a -> s -> m s仅举轮的最终状态。这样,您就不需要使用let绑定提取得分,并且可以做到这一点内嵌代替:(personS,compS) <- execStateT morra (0,0)
  • ['P',':',' ',p]可以写为("P: " ++ [p])

它的风格和偏好的问题,但

if condition 
    then do 
    doSomethingA 
    doSomethingB 
    else someFunction $ do 
    doSomethingElseA 
    doSomethingElseB 

总的来说,不错:)

01:您可以通过重新安排 if S, else S和 do■减少了很多的缩进和格式化空白
+0

感谢您的。不过,我需要Morra函数的循环。函数main只能运行一次。如果我将'morra'放在函数的末尾(与'when'后面的'do'一致),则什么都不会发生。 – user1897830

+0

@ user1897830你需要把它和'when'(只缩进两个空格)放在一起。如果你把它放在when中,它只会在'isDigit p'为true时循环。 “什么都没有发生”是什么意思?它会立即退出,还是会陷入循环? – Lazersmoke

+0

我需要它仅在'isDigit p'为true时循环。这是循环直到'isDigit p'不正确,然后退出。但是,当我把morra放在它刚刚退出并且不循环的时候。 – user1897830

1

我换成p <- liftIO getCharp <- liftIO getLine,并提出了其他一些小的改动,以允许事实是p现在是一个字符串,而不是一个字符。现在它可以工作。似乎它与Windows有关,因为它在linux上使用getChar。这是最后的代码:

module Morra where 

import Control.Monad.Trans.State.Lazy 
import Control.Monad.IO.Class 
import Data.Char (isDigit, digitToInt) 
import System.Random (randomRIO) 
import Control.Monad (when) 

morra :: StateT (Int, Int) IO() 
morra = do 
    p <- liftIO getLine 
    let p1 = head p 
    when (isDigit p1) $ do 
    let p' = digitToInt p1 
    c <- liftIO $ randomRIO (1, 2) 
    liftIO $ putStrLn ("P: " ++ p) 
    liftIO $ putStrLn ("C: " ++ show c) 
    (pt, ct) <- get 
    if even (c + p') then do 
     liftIO $ putStrLn "Computer Wins" 
     put (pt, ct + 1) 
    else do 
     liftIO $ putStrLn "Player Wins" 
     put (pt + 1, ct) 
    morra 

main :: IO() 
main = do 
    putStrLn "-- p is Player" 
    putStrLn "-- c is Computer" 
    putStrLn "-- Player is odds, Computer is evens." 
    (personS,compS) <- execStateT morra (0,0) 
    putStrLn ("Person Score: " ++ show personS) 
    putStrLn ("Computer Score: " ++ show compS) 
    if personS == compS then 
    putStrLn "No Winner" 
    else if personS > compS then 
    putStrLn "Winner is Person" 
    else 
    putStrLn "Winner is Computer"