2012-01-23 11 views
5

Nie rozumiem, dlaczego ten kod wykonuje pętlę tylko raz, a następnie kończy działanie? w Ghci mogę odpowiedzieć tylko na pierwszą pętlę, wtedy wydaje się, że zmienna cont jest ustawiona na false i nie mam zachęty do odpowiedzi.Pętla z StateT: Dlaczego ta pętla nie tworzy pętli

Wynikiem jest:

*Main> testLoop1 td10 
test 
Do you want to continue? (y/N) 
y 
we continue 
test 
Do you want to continue? (y/N) 
We stop 

Kod:

type TDeckSTIO = StateT TableDecks IO 

continue = do 
putStrLn "Do you want to continue? (y/N)" 
c <- getChar 
return $ c == 'y' 


loop1 :: TDeckSTIO() 
loop1 = do 
    liftIO $ putStrLn "test" 
    cont<- liftIO continue 
    if cont 
    then do 
     liftIO $ putStrLn "we continue" 
     liftIO $ testLoop1 td 

    else liftIO $ putStrLn "We stop" 

testLoop1 td = runStateT (loop1) td >> return() 

Odpowiedz

15

Problem polega na tym, że po wpisaniu y i wciśnij enter, że faktycznie wpisując dwa znaki: Sam 'y', i znak nowego wiersza, który zostanie wysłany przez naciśnięcie klawisza powrotu. Za pierwszym razem pętla widzi 'y', ale następnym razem widzi '\n', a ponieważ '\n' nie jest 'y', kończy działanie.

Można to zrobić hSetBuffering stdin NoBuffering przed podaj swój pętlę (będziesz musiał importować System.IO), co pozwoli przetwarzać znaków bez czekania na nową linią, lub specjalnie przetwarzać linii naraz:

continue = do 
    putStrLn "Do you want to continue? (y/N)" 
    s <- getLine 
    return $ s == "y" 

Nawiasem mówiąc, zamiast pisać liftIO $ testLoop1 td, możesz po prostu pozostać w tej samej monadzie stanu: możesz zastąpić ją loop1 i będzie działać dokładnie tak samo.

Również testLoop1 jest lepiej napisany jako:

testLoop1 = evalStateT loop1 

evalStateT jest jak runStateT, ale nie obejmuje stan końcowy, więc nie trzeba jednoznacznie odrzucić wartość z >> return().

+0

Dzięki dokładnie to, co chciałem i więcej. Byłem na ścieżce znalezienia, kiedy właśnie zmieniłem kod i dodałem druk s, który pokazuje '\ n'. Dzięki za wskazówkę na liftIO $ testLoop1 td -> loop1. To był jeden z moich poprzednich testów i zastanawiałem się, który z tych dwóch kodów należy zastąpić. I dobry punkt z evalState. To było moje pierwsze zetknięcie z statetransformer i IO. –