tipos sentencia que programacion lenguaje estructuras estructura else ejemplos decisiones decision control anidados haskell coding-style

haskell - que - sentencia if en c++



¿Buen estilo de codificación Haskell del bloque de control if/else? (8)

Estoy aprendiendo que Haskell espera que me permita acercarme más a la programación funcional, antes de aprenderla, utilizo principalmente C-sytanx como los lenguajes, como C, Java o D Programming Language.

Seguí el tutorial en Wikilibro , va bien hasta ahora, pude entender la mayoría de ellos antes del capítulo "Entrada y salida simple"

Pero tengo una pequeña pregunta sobre el estilo de codificación del bloque de control if / else utilizado por el tutorial.

En wikibook, el código se parece a lo siguiente:

doGuessing num = do putStrLn "Enter your guess:" guess <- getLine if (read guess) < num then do putStrLn "Too low!" doGuessing num else if (read guess) > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!"

Me hace confuso, porque este estilo de codificación es totalmente volador "Good Coding Style" en C-sytnax como lenguaje de programación, donde deberíamos identificar if / else if / else en la misma columna.

Sé que simplemente no funciona en Haskell, porque causaría un error de análisis si identifico "else" en la misma columna de "si".

Pero, ¿y el siguiente? Creo que es mucho más claro que el anterior. Pero como lo anterior es utilizado por Wikibook y Yet Another Haskell Tutorial, que marcó "el mejor tutorial disponible en línea" en el sitio oficial de Haskell, no estoy seguro de si este estilo de codificación es una convención en los programas de Haskell.

doGuessing num = do putStrLn "Enter your guess:" guess <- getLine if (read guess) < num then do putStrLn "Too low!" doGuessing num else if (read guess) > num then do putStrLn "Too high!" doGuessing num else do putStrLn "You Win!"

Entonces, tengo curiosidad sobre qué estilo de codificación se usa con más frecuencia o si hay un estilo de codificación anthoer para este fragmento de código. Me gustaría saberlo también


¡El estilo Haskell es funcional, no imprescindible! En lugar de "hacer esto luego eso", piense en combinar funciones y describir lo que hará su programa, no cómo.

En el juego, su programa le pide al usuario una suposición. Una suposición correcta es un ganador. De lo contrario, el usuario intenta nuevamente. El juego continúa hasta que el usuario adivine correctamente, por lo que escribimos que:

main = untilM (isCorrect 42) (read `liftM` getLine)

Esto usa un combinador que ejecuta una acción repetidamente ( getLine extrae una línea de entrada y read convierte esa cadena a un entero en este caso) y verifica su resultado:

untilM :: Monad m => (a -> m Bool) -> m a -> m () untilM p a = do x <- a done <- p x if done then return () else untilM p a

El predicado (parcialmente aplicado en main ) verifica la conjetura contra el valor correcto y responde en consecuencia:

isCorrect :: Int -> Int -> IO Bool isCorrect num guess = case compare num guess of EQ -> putStrLn "You Win!" >> return True LT -> putStrLn "Too high!" >> return False GT -> putStrLn "Too low!" >> return False

La acción que se ejecutará hasta que el jugador adivine correctamente es

read `liftM` getLine

¿Por qué no mantenerlo simple y simplemente componer las dos funciones?

*Main> :type read . getLine <interactive>:1:7: Couldn''t match expected type `a -> String'' against inferred type `IO String'' In the second argument of `(.)'', namely `getLine'' In the expression: read . getLine

El tipo de getLine es IO String , pero read quiere una String pura.

La función liftM de Control.Monad toma una función pura y la "levanta" en una mónada. El tipo de expresión nos dice mucho sobre lo que hace:

*Main> :type read `liftM` getLine read `liftM` getLine :: (Read a) => IO a

Es una acción de E / S que cuando se ejecuta nos devuelve un valor convertido con read , un Int en nuestro caso. Recuerde que readLine es una acción de E / S que produce valores de String , por lo que puede pensar en que liftM nos permite aplicar la read "dentro" de la mónada IO .

Juego de muestra:

1 Too low! 100 Too high! 42 You Win!


La forma en que Haskell interpreta if ... then ... else dentro de un bloque do está muy de acuerdo con la sintaxis de Haskell.

Pero muchas personas prefieren una sintaxis ligeramente diferente, permitiendo then que aparezca en el mismo nivel de sangría que el if correspondiente. Por lo tanto, GHC viene con una extensión de lenguaje DoAndIfThenElse llamada DoAndIfThenElse , que permite esta sintaxis.

La extensión DoAndIfThenElse forma parte del lenguaje central en la última revisión de la especificación Haskell , Haskell 2010 .


Puedes usar la construcción "caso":

doGuessing num = do putStrLn "Enter your guess:" guess <- getLine case (read guess) of g | g < num -> do putStrLn "Too low!" doGuessing num g | g > num -> do putStrLn "Too high!" doGuessing num otherwise -> do putStrLn "You Win!"



Tenga en cuenta que el hecho de que tenga que sangrar el ''entonces'' y ''else'' dentro de un bloque ''do'' se considera un error por muchos. Probablemente se solucionará en Haskell ''(Haskell prime), la próxima versión de la especificación Haskell.


Una mejora menor a la declaración de caso de mattiast (editaría, pero me falta el karma) es usar la función de comparación, que devuelve uno de los tres valores, LT, GT o EQ:

doGuessing num = do putStrLn "Enter your guess:" guess <- getLine case (read guess) `compare` num of LT -> do putStrLn "Too low!" doGuessing num GT -> do putStrLn "Too high!" doGuessing num EQ -> putStrLn "You Win!"

Realmente me gustan estas preguntas de Haskell, y animo a otros a publicar más. A menudo sientes que debe haber una mejor manera de expresar lo que estás pensando, pero Haskell es inicialmente tan extraño que no se te ocurrirá nada.

Pregunta extra para el periodista de Haskell: ¿cuál es el tipo de doGuessing?


Utilizo un estilo de codificación como tu ejemplo de Wikilibros. Claro, no sigue las pautas de C, pero Haskell no es C, y es bastante legible, especialmente una vez que te acostumbras. También está modelado según el estilo de los algoritmos utilizados en muchos libros de texto, como Cormen.


Verás un montón de diferentes estilos de sangría para Haskell. La mayoría de ellos son muy difíciles de mantener sin un editor que esté configurado para sangrar exactamente en cualquier estilo.

El estilo que muestra es mucho más simple y menos exigente para el editor, y creo que debería quedarse con él. La única incoherencia que puedo ver es que pones el primer do en su propia línea mientras pones el otro dos después del then / else.

Presta atención a los otros consejos sobre cómo pensar sobre el código en Haskell, pero mantén tu estilo de sangría.