30

関数型プログラミングに近づくのに役立つことを期待して、Haskell を学んでいます。以前は、C、Java、D など、C に似た構文の言語を主に使用していました。

Wikibooks のチュートリアルでif使用されている/elseコントロール ブロックのコーディング スタイルについて少し質問があります。コードは次のようになります。

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

このコーディング スタイルは、C ライクな言語で推奨されるスタイルに完全に違反しているため、混乱ifelse ifますelse

elseと同じ列でインデントすると解析エラーになるため、Haskellでは機能しないことはわかっていますif

しかし、次のスタイルはどうでしょうか。上記より分かりやすいと思います。しかし、上記は Wikibooks と、Haskell の公式 Web サイトで「オンラインで入手できる最高のチュートリアル」とマークされている Yet Another Haskell Tutorial で使用されているため、このコーディング スタイルが 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!"

では、どのコーディング スタイルがより頻繁に使用されているのか、またはこのコードの別のコーディング スタイルはあるのでしょうか?

4

8 に答える 8

27

Haskellスタイルは機能的であり、必須ではありません!「これを実行してからそれを実行する」のではなく、関数を組み合わせて、プログラムがどのように実行するかではなく、何を実行するかを説明することを検討してください。

ゲームでは、プログラムはユーザーに推測を求めます。正しい推測が勝者です。それ以外の場合、ユーザーは再試行します。ゲームはユーザーが正しく推測するまで続くので、次のように記述します。

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

これは、アクションを繰り返し実行するコンビネータを使用し(この場合getLine、入力の行をプルしreadてその文字列を整数に変換します)、その結果をチェックします。

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

述語(で部分的に適用されmainます)は、正しい値に対して推測をチェックし、それに応じて応答します。

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

プレイヤーが正しく推測するまで実行されるアクションは

read `liftM` getLine

シンプルにして、2つの機能を構成してみませんか?

* Main>:typeread。getLine

<インタラクティブ>:1:7:
    期待されるタイプ`a->String'と一致しませんでした
           推論されたタイプ`IO文字列'に対して
    `(。)'の2番目の引数、つまり` getLine '
    式の中で:read。getLine

のタイプはgetLineですが、純粋なIO Stringが必要です。readString

Control.Monadの関数liftMは純粋関数を取り、それをモナドに「リフト」します。式のタイプは、それが何をするかについて多くのことを教えてくれます。

* Main>:type read `liftM` getLine
読み取り`liftM`getLine ::(読み取りa)=> IO a

これはI/Oアクションであり、実行すると、この場合は、で変換された値が返さreadIntます。これは値readLineを生成するI/Oアクションであるため、モナドの「内部」に適用Stringできると考えることができます。liftMreadIO

サンプルゲーム:

1
低すぎる!
100
高すぎる!
42
あなたが勝ちます!
于 2010-01-19T18:52:21.287 に答える
8

mattiast の case ステートメント (編集したいのですが、カルマがありません) の小さな改善点は、LT、GT、または EQ の 3 つの値のいずれかを返す比較関数を使用することです。

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

私はこれらの Haskell の質問がとても気に入っています。他の人にももっと投稿してもらいたいです。考えていることを表現するにはもっと良い方法が必要だと思うことよくありますが、Haskell は最初はあまりにもなじみがなく、何も思い浮かびません。

Haskell ジャーニーマンへのボーナス質問: doGuessing の型は何ですか?

于 2008-09-29T01:46:11.493 に答える
8

「ケース」構造を使用できます。

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!"
于 2008-09-24T21:58:54.610 に答える
3

「do」ブロック内の「then」と「else」をインデントする必要があるという事実は、多くの人がバグと見なしていることに注意してください。これは、Haskell 仕様の次のバージョンである Haskell' (Haskell prime) でおそらく修正されるでしょう。

于 2008-11-11T23:40:41.743 に答える
1

中かっこで明示的なグループ化を使用することもできます。http://www.haskell.org/tutorial/patterns.htmlのレイアウト セクションを参照してください。

私はそれをお勧めしません。いくつかの特別な場合を除いて、明示的なグループ化を使用している人を見たことがありません。私は通常、スタイルの例としてStandard Prelude コードを調べます。

于 2008-09-24T13:51:17.420 に答える
0

Haskell のさまざまなインデント スタイルが表示されます。それらのほとんどは、どのようなスタイルでも正確にインデントするように設定されたエディターなしでは維持するのが非常に困難です。

あなたが表示するスタイルははるかにシンプルで、エディターの要求が少ないので、それに固執する必要があると思います. 私が見ることができる唯一の矛盾は、最初の do を独自の行に配置し、他の dos を then/else の後に配置することです。

Haskell でのコードの考え方については、他のアドバイスに注意してください。ただし、インデントのスタイルには固執してください。

于 2010-08-29T10:21:01.223 に答える
0

Wikibooks の例のようなコーディング スタイルを使用します。確かに、C のガイドラインには従っていませんが、Haskell は C ではなく、特に慣れればかなり読みやすくなっています。また、Cormen などの多くの教科書で使用されているアルゴリズムのスタイルに基づいています。

于 2008-10-31T19:36:42.080 に答える