7

私はまだ Haskell に苦労していますが、この例の Input/Output モナドに頭を悩ませる問題に遭遇しました。

main = do   
line <- getLine  
if null line  
    then return ()  
    else do  
        putStrLn $ reverseWords line  
        main  
  
reverseWords :: String -> String  
reverseWords = unwords . map reverse . words

Haskell のような関数型言語は関数の副作用に基づくことができないため、何らかの解決策を考案する必要があったことは理解しています。この場合、すべてをdoブロックにラップする必要があるようです。簡単な例が得られますが、この場合、誰かの説明が本当に必要です:

  1. I/O アクションに単一のdoブロックを使用するだけでは十分でないのはなぜですか?
  2. if/else ケースで完全に新しいものを開く必要があるのはなぜですか?
  3. また、モナドの「スコープ」がdo終了するのはいつですか?つまり、標準のHaskell用語/関数を使用できるのはいつですか?
4

2 に答える 2

8

doブロックは、最初のステートメントと同じインデント レベルにあるものすべてに関係します。したがって、あなたの例では、実際には2つのものをリンクしているだけです:

 line <- getLine

残りのすべては、たまたまかなり大きくなります。

 if null line  
  then return ()
  else do
      putStrLn $ reverseWords line  
      main  

しかし、どんなに複雑であっても、構文はこれらの式do調べません。したがって、これはすべてまったく同じです

main :: IO ()
main = do
   line <- getLine
   recurseMain line

ヘルパー関数で

recurseMain :: String -> IO ()
recurseMain line
   | null line  = return ()
   | otherwise  = do
           putStrLn $ reverseWords line
           main

ここで、関数が main からのブロック内で呼び出されていることを明らかにスタッフrecurseMainが認識できないdoため、別の を使用する必要がありますdo

于 2014-06-18T20:12:22.207 に答える
8

do実際には何もしません。ステートメントを簡単に結合するための構文糖衣です。疑わしい類推は、次のものと比較するdoこと[]です。

複数の式がある場合は、次を使用してそれらをリストに結合できます:

(1 + 2) : (3 * 4) : (5 - 6) : ...

ただし、これは煩わしいので、代わりに[]表記法を使用できます。これは同じものにコンパイルされます。

[1+2, 3*4, 5-6, ...] 

同様に、複数の IO ステートメントがある場合は、 andを使用してそれらを組み合わせることができ>>>>=ます。

(putStrLn "What's your name?") >> getLine >>= (\name -> putStrLn $ "Hi " ++ name)

ただし、これは煩わしいので、代わりにdo表記法を使用できます。これは同じものにコンパイルされます。

do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn $ "Hi " ++ name

複数のdoブロックが必要な理由に対する答えは簡単です。

値のリストが複数ある場合は、複数[]の が必要です (ネストされている場合でも)。

モナド文のシーケンスが複数ある場合は、複数doの s が必要です (ネストされている場合でも)。

于 2014-06-18T20:12:47.037 に答える