30 votes

Bon style de codage Haskell du bloc de contrôle if / else?

Je suis en train d'apprendre Haskell espère que ça pourrait me permettre de se rapprocher de la programmation fonctionnelle, avant de déblai, j'utilise principalement la C-sytanx comme les langages, comme le C, le Java ou le D Langage de Programmation.

J'ai suivi le tutoriel sur Wikibook, il va bien jusqu'à présent, je pouvais comprendre la plupart d'entre eux avant le chapitre "Simple d'entrée et de sortie"

Mais j'ai une petite question concernant le style de codage de if/else bloc de contrôle utilisé par le tutoriel.

Dans le wikibook, le code ressemblera à ceci:

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!"

Il me fait de la confusion, car ce style de codage est totalement volate "Bon Style de Codage" en C-sytnax comme langage de programmation, où l'on doit ident if/else if/else dans la même colonne.

Je le sais tout simplement pas travailler en Haskell, parce qu'il serait la cause d'erreur d'analyse si je ident "else" au même colonne de "si".

Mais quel est le suivant? Je pense que c'est beaucoup plus clair la-dessus. Mais depuis que le ci-dessus est utilisé par Wikibook et Encore un Autre Tutorial Haskell, qui a marqué le "meilleur tutoriel disponible en ligne à l'officiel Haskell site, donc je ne suis pas sûr de savoir si ce style de codage est une convention en Haskell programmes.

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!"

Donc, je suis curieux de savoir lequel style de codage est utilisé le plus souvent ou est-il anthoer style de codage pour ce morceau de code? Je voudrais savoir aussi.

27voto

Greg Bacon Points 50449

Haskell style est fonctionnel, pas impératif! Plutôt que de "faire cela, alors que," penser sur la combinaison de fonctions et de décrire ce que votre programme va faire, pas comment.

Dans le jeu, votre programme demande à l'utilisateur pour une deviner. Une supposition correcte, c'est un gagnant. Sinon, l'utilisateur essaie de nouveau. Le jeu continue jusqu'à ce que l'utilisateur trouve la bonne réponse, nous avons donc écrire que:

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

Il utilise un combinateur à plusieurs reprises exécute une action (getLine tire une ligne d'entrée et de read convertit la chaîne en un entier, dans ce cas) et vérifie son résultat:

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

Le prédicat (partiellement appliquée en main) vérifie le deviner à l'encontre de la valeur correcte et répond en conséquence:

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

L'action à exécuter jusqu'à ce que le lecteur devine correctement est

read `liftM` getLine

Pourquoi ne pas faire simple et juste de composer les deux fonctions?

*Principal> :type de lecture . getLine

<interactif>:1:7:
 Ne pouvais pas faire correspondre le type `a -> String'
 contre déduit de type `IO String'
 Dans le deuxième argument de `(.)', à savoir "getLine'
 Dans l'expression: lire . getLine

Le type d' getLine est IO String, mais read veut un pur String.

La fonction liftM de Contrôle.Monade prend une fonction pure et "ascenseurs" en une monade. Le type de l'expression en dit beaucoup sur ce qu'il fait:

*Principal> :type de la lecture " liftM` getLine
lire " liftM` getLine :: (Lu) => IO un

C'est un I/O action à exécuter lorsque nous redonne une valeur convertie avec read, une Int dans notre cas. Rappelons qu' readLine I/O action que les rendements en String valeurs, de sorte que vous pouvez penser, liftM comme nous permettant d'appliquer un read "à l'intérieur" de la IO monade.

Exemple de jeu:

1
Trop faible!
100
Trop élevé!
42
Vous Gagnez!

8voto

Peter Burns Points 17420

Une légère amélioration de la mattiast le cas de l'énoncé (j'avais modifier, mais je n'ai pas le karma) est d'utiliser la fonction de comparaison, qui renvoie l'une des trois valeurs, LT, GT, ou 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!"

J'aime vraiment ces Haskell questions, et j'encourage les autres à poster plus. Souvent vous vous sentez comme il y a eu le meilleur moyen d'exprimer ce que vous pensez, mais Haskell est initialement étranger que rien ne viendra à l'esprit.

Question Bonus pour le Haskell journyman: quel est le type de doGuessing?

8voto

mattiast Points 1482

Vous pouvez utiliser la structure "case":

 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!"
 

4voto

yfeldblum Points 42613

La façon Haskell interprète if ... then ... else dans un do bloc est très en accord avec l'ensemble de Haskell syntaxe.

Mais beaucoup de gens préfèrent une syntaxe légèrement différente, permettant then et else apparaissent au même niveau d'indentation que le if. Par conséquent, GHC est livré avec un opt-in extension du langage appelés DoAndIfThenElse, ce qui permet à cette syntaxe.

L' DoAndIfThenElse extension est faite dans le cadre de la base de la langue dans la dernière révision de la Haskell spécification, Haskell 2010.

3voto

Erik Hesselink Points 1570

Notez que le fait que vous ayez à mettre en retrait le 'puis' et le 'autre' à l'intérieur d'un bloc 'do' est considéré par beaucoup comme un bug. Il sera probablement corrigé dans Haskell '(Haskell prime), la prochaine version de la spécification Haskell.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X