3

別の関数の入力としてこの関数からタプルを取得したいので、IO()関数はタプルを返すことができるのだろうか。

investinput :: IO()->([Char], Int)
investinput = do
 putStrLn "Enter Username : "
 username <- getLine

 putStrLn "Enter Invest Amount : "
 tempamount <- getLine
 let amount = show  tempamount
 return (username, amount)

助けてください。

ありがとう。

4

4 に答える 4

8

Haskell の IO は、使い慣れた言語の IO のようには機能しません。Haskell のすべての関数は純粋でなければなりません。つまり、関数fが引数xで呼び出される場合、1 回、2 回、または 100 回呼び出しても違いがあってはなりません。ただし、これが IO にとって何を意味するかを考えてみましょう。単純にgetLine、タイプgetLine :: Stringが 、またはおそらくである必要がありgetLine :: () -> Stringます。(()は unit 型で、値は のみです()。C ライクな言語の void 型のようなものですが、値は 1 つしかありません。) しかし、これは、 を書くたびにgetLine、 を返さなければならないことを意味します。同じ string、これはあなたが望むものではありません。これが型の目的です:アクションIOをカプセル化する. これらのアクションは関数とは異なります。それらは不純な計算を表します (それら自体は純粋ですが)。type の値はIO a、実行時に type の値を返すアクションを表しますa。したがって、getLinehas type getLine :: IO String: アクションが評価されるたびに、String(ユーザーからの読み取りによって) が生成されます。同様に、putStrタイプがputStr :: String -> IO ()あります。これは、文字列を受け取り、実行時に有用な情報を返さないアクションを返す関数ですが、副作用として、画面に何かを出力します。

タイプ の関数を記述しようとしていますIO () -> ([Char], Int)。これは、入力としてアクションを取り、タプルを返す関数になりますが、これはあなたが望むものではありません。実行時に、文字列 ( のシノニム) と整数IO (String, Int)で構成されるタプルを生成するアクションが必要です。[Char]あなたも現在のコードでほとんどそこにいます! 代わりに必要なものは次のとおりです。

investinput :: IO (String, Int)
investinput = do
  putStrLn "Enter Username : "
  username <- getLine
  putStrLn "Enter Invest Amount : "
  tempamount <- getLine
  let amount = read tempamount
  return (username, amount)

2 つの変更 (および空白行の削除) しか行っていないことに注意してください。まず、上で述べたように、関数の型を変更しました。次に、 に変更showしましreadた。関数のshowShow a => a -> Stringは : 表示可能なものすべてを取り、それを表す文字列を生成する関数です。 タイプread: 文字列を指定すると、Read a => String -> aそれを解析し、読み取り可能な値を返します。

あなたが尋ねたもう1つのこと(String, Int)は、アクションの代わりにタプルを返すことですIO (String, Int)。これを行う純粋な方法はありません。つまり、純粋な関数はありませんIO a -> a。どうしてこれなの?なぜならIO a、現実世界に依存する不純な行為を表しているからです。そのような関数がある場合、その関数は純粋でなければならないので、impossibleRunIO :: IO a -> aそのようにしたいと思います。しかし、現実の世界と実際にやり取りできるようにしimpossibleRunIO getLine == impossibleRunIO getLineたいので、これは役に立ちません! impossibleRunIOしたがって、この純粋な関数は不可能です。入ったものIOは決して出ることはありません。これが何をするかreturnです: これは、この場合は1の型を持つ関数であり、return :: a -> IO a純粋な値を に配置できるようにしますIO。についてはxreturn xは、実行時に常に を生成するアクションxです。doこれが、ブロックをreturn:で終了する必要がある理由です。usernameアクションから抽出した純粋な値であるため、doブロック内でのみ表示されます。IO外の世界がそれを見ることができる前に、それを持ち上げる必要があります。amount/も同様ですtempamount

完全を期すために、この背後にはいくつかの包括的な理論があり、それを結び付けています。しかし、Haskell プログラミングを始めるにはまったく必要ありません。私がお勧めするのは、ほとんどのコードを純粋な関数として構造化して、データを折りたたんだり、紡錘したり、切断したりすることです。次に、前述の機能と相互作用する薄い (できるだけ薄い)IOフロント レイヤーを構築します。必要な IO の少なさに驚かれることでしょう。

1: 実際にはもっと一般的なタイプがありますが、現時点では関係ありません。

于 2010-06-18T03:59:08.173 に答える
4

ええ、あなたはほとんどそこにいますが、私はあなたが署名が欲しいと思います:

investinput :: IO ([Char], Int)

...次に、呼び出し元の関数から、次のようなことができます。

main = do
    (username, amount) <- investinput
    ....

でも見せるのではなく、tempamountを読みたいと思います。

于 2010-06-18T03:15:50.743 に答える
2

タプルを生成するIO関数は、次のようなタイプIO (a, b)になります。

investinput :: IO ([Char], Int)

のシグニチャはIO () -> ([Char], Int)、関数が型のパラメータを受け取り、IO ()そこからタプルを生成することを意味しますが、これは必要なものではありません。

通常、IO関数(または別のモナドの関数)が返すことができるタイプに制限はありません。任意のタイプを選択できます。

于 2010-06-18T03:21:43.633 に答える
1

返品についての質問への答えは簡単です。返品できません(String, Int)IO (String, Int)あなたがそこに入ると、IOそこに立ち往生しています。これは、Haskell が「純粋な」言語であると人々が言うときの意味の一部です。

あなたがしたいことは、あなたがすでにここで で行っていることと似ていますgetLine。の型はgetLineですIO String。を書くときusername <- getLine、実質的に を取り出していますStringIO String、これは式の中にいるからこそ可能ですdo

とまったく同じことができinvestinputますgetLine。関数で使用investinputする方法の例を次に示します。main

main = do
  (name, amount) <- investinput
  putStrLn $ name ++ " invested $" ++ show amount ++ "."

コメントで言及したので、表記の代わりにリバース バインド演算子 ( )liftMを使用して同じことを行う完全に機能するバージョンを次に示します。liftM=<<do

import Control.Monad (liftM)

investinput :: IO (String, Int)
investinput = do
   putStrLn "Enter Username : "
   username <- getLine
   putStrLn "Enter Invest Amount : "
   amount <- liftM read getLine -- We can also use liftM to get rid of tempamount.
   return (username, amount)

summary :: (String, Int) -> String
summary (name, amount) = name ++ " invested $" ++ show amount ++ "."

main = putStrLn =<< liftM summary investinput

investinputこれは、「タプルを期待する別の関数」でどのように使用できるかを示しています。

于 2010-06-18T04:03:53.797 に答える