simbolos - instancias en haskell
IO sucede fuera de orden al usar getLine y putStr (3)
Soy un principiante de Haskell, apenas estoy empezando a envolver mi cabeza alrededor de Monads, pero todavía no lo entiendo. Estoy escribiendo un juego que consiste en pedirle al usuario su opinión y responder. Aquí está una versión simplificada de mi función:
getPoint :: IO Point
getPoint = do
putStr "Enter x: "
xStr <- getLine
putStr "Enter y: "
yStr <- getLine
return $ Point (read xStr) (read yStr)
completeUserTurn :: (Board, Player) -> IO (Board, Player)
completeUserTurn (board, player) = do
putStr $ "Enter some value: "
var1 <- getLine
putStr $ "Enter another value: "
var2 <- getLine
putStr $ "Enter a point this time: "
point <- getPoint
if (... the player entered legal values ...) then do
putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) "
continue <- getLine
if continue == "y" then
return (...updated board..., ...updated player...)
else
completeUserTurn (board, player)
else do
putStr "Invalid Move!/n"
completeUserTurn (board, player)
Lo que está sucediendo es que las indicaciones aparecerán desordenadas con el texto que debe aparecer antes de la solicitud.
Aquí hay un ejemplo de lo que está pasando después de que compilé el código anterior:
1
Ingrese algún valor: Ingrese otro valor: 2
3
4
Ingrese un punto esta vez: Ingrese x: Ingrese y: y
¿Es esto correcto? (y / n):
Las negritas son las cosas que escribí.
Obviamente, tengo un error conceptual importante, pero no sé qué. Tenga en cuenta que funciona correctamente en el intérprete y falla cuando se compila.
Como dijo Michael, el problema es amortiguar. De forma predeterminada, la salida se almacena en búfer hasta que imprime una nueva línea (o hasta que el búfer está lleno si tiene líneas realmente largas), por lo que la mayoría de las veces verá este problema cuando intente hacer indicaciones en la misma línea usando putStr
como lo está haciendo. .
Sugiero que defina una pequeña función de ayuda como esta para encargarse de hacer el lavado por usted:
import System.IO
prompt :: String -> IO String
prompt text = do
putStr text
hFlush stdout
getLine
Ahora puedes simplemente hacer
getPoint = do
xStr <- prompt "Enter x: "
yStr <- prompt "Enter y: "
return $ Point (read xStr) (read yStr)
El IO está sucediendo en el orden correcto. El problema es el almacenamiento en búfer. Si elimina la salida estándar después de cada putStr, debería funcionar como se esperaba. Deberá importar hFlush
y stdout
desde System.IO
.
El problema no fue con el orden de las operaciones en el código IO. El problema fue que la entrada y la salida están almacenadas de manera predeterminada en el búfer cuando se usan stdin y stdout. Esto aumenta el rendimiento de IO en una aplicación, pero puede hacer que las operaciones parezcan desordenadas cuando se usan tanto stdin como stdout.
Hay dos soluciones para esto. Puede usar el método hFlush
para forzar la hFlush
de un tirador (ya sea estándar o estándar). Por ejemplo, hFlush stdout
, hFlush stdin
. Una solución más simple (que funciona bien para aplicaciones interactivas) es deshabilitar el almacenamiento en búfer por completo. Puede hacer esto llamando a los métodos hSetBuffering stdout NoBuffering
y hSetBuffering stdin NoBuffering
antes de iniciar su programa (es decir, ponga esas líneas en su método principal.