Pergunta

Estou aprendendo Haskell na esperança de que ele vai me ajudar a chegar mais perto de programação funcional. Anteriormente, eu principalmente idiomas usado com C-como sintaxe, como C, Java, e D.

Eu tenho uma pequena dúvida sobre o estilo de codificação de um bloco de controle if / else usado pelo tutorial sobre Wikibooks . Os olhares código como o seguinte:

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

Isso me deixa confuso, porque este estilo de codificação viola totalmente o estilo recomendado em C-como línguas, onde devemos endentar if, else if e else na mesma coluna.

Eu sei que simplesmente não funciona em Haskell, porque seria um erro de análise se eu recuado else na mesma coluna que if.

Mas o que dizer o seguinte estilo? Eu acho que é muito mais claro do que o descrito acima. Mas desde que o acima é usada por Wikibooks e ainda um outro Haskell Tutorial, que é marcado "melhor tutorial disponível on-line" no site oficial Haskell, eu não tenho certeza se este estilo de codificação é uma convenção em programas 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!"

Então, estou curioso sobre qual estilo de codificação é usado mais frequentemente ou existe um outro estilo de codificação para este pedaço de código?

Foi útil?

Solução

estilo Haskell é funcional, não é imperativo! Ao invés de "fazer isso, então, que," pensar sobre como combinar funções e descrevendo o seu programa vai fazer, não como.

No jogo, o programa pede ao usuário para uma suposição. Um palpite correto é um vencedor. Caso contrário, o usuário tenta novamente. O jogo continua até que as suposições de usuário corretamente, então nós escrevemos que:

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

Isto usa um combinadores que repetidamente executa uma acção (getLine puxa uma linha de entrada e convertidos read que a cadeia de um número inteiro, neste caso) e verifica o 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

O predicado (parcialmente aplicado em main) verifica a suposição contra o valor correcto e responde em conformidade:

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

A ação a ser executado até que o jogador adivinha corretamente é

read `liftM` getLine

Por que não mantê-lo simples e apenas compor as duas funções?

*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

O tipo de getLine é IO String, mas read quer um String puro.

A função liftM partir Control.Monad leva uma função pura e “elevadores”-lo em uma mônada. O tipo da expressão nos diz muito sobre o que ele faz:

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

É uma ação de I / O que nos quando executado devolve um valor convertido com read, um Int no nosso caso. Lembre-se que readLine é uma ação de I / O que os rendimentos String valores, de modo que você pode pensar liftM como o que nos permite aplicar read “dentro” da mônada IO.

jogo Amostra:

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

Outras dicas

Você pode usar o "caso" -construct:

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

Uma pequena melhoria a declaração caso de mattiast (eu editar, mas me falta a karma) é usar a função de comparação, que retorna um dos três valores, 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!"

Eu realmente gosto dessas perguntas Haskell, e eu incentivar outras pessoas a postar mais. Muitas vezes você se sente como se houvesse tem para ser uma maneira melhor de expressar o que você está pensando, mas Haskell é inicialmente tão estranho que nada virá à mente.

Bônus pergunta para a journyman Haskell: qual é o tipo de doGuessing

A forma Haskell interpreta if ... then ... else dentro de um bloco do está muito em sintonia com toda a sintaxe do Haskell.

Mas muitas pessoas preferem uma sintaxe ligeiramente diferente, permitindo then e else a aparecer ao mesmo nível de recuo como o if correspondente. Portanto, GHC vem com um opt-in de extensão linguagem chamada DoAndIfThenElse, que permite que esta sintaxe.

A extensão DoAndIfThenElse é feita em parte da linguagem núcleo na última revisão da especificação Haskell, Haskell 2010 .

Note que o fato de que você tem que recuar o 'depois' e 'else' dentro de um bloco 'fazer' é considerado um bug por muitos. Ele provavelmente será corrigido em Haskell'(Haskell prime), a próxima versão da especificação Haskell.

Você também pode usar agrupamento explícita com chaves. Consulte a seção layout do http://www.haskell.org/tutorial/patterns.html

Eu não recomendaria que embora. Eu nunca vi ninguém uso agrupamento explícita além de em alguns casos especiais. Eu costumo olhar para o Padrão Prelude código para exemplos de estilo.

Eu uso um estilo de codificação como o seu exemplo de Wikibooks. Claro, ele não seguir as orientações C, mas Haskell não é C, e é bastante legível, especialmente uma vez que você se acostumar com isso. Ele também é modelada após o estilo de algoritmos usados ??em muitos livros didáticos, como Cormen.

Você vai ver um monte de diferentes estilos de recuo para Haskell. A maioria deles são muito difíceis de manter sem um editor que está configurado para travessão exatamente em qualquer estilo.

O estilo que você exibir é muito mais simples e menos exigente do editor, e eu acho que você deveria ficar com ela. A única inconsistência eu posso ver é que você colocar a primeira fazer em sua própria linha enquanto você colocar os outros dos após o then / else.

Preste atenção a outros conselhos sobre a forma de pensar sobre o código em Haskell, mas vara para o seu estilo de recuo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top