Haskell Language
IO
サーチ…
標準入力のすべての内容を文字列に読み込む
main = do
input <- getContents
putStr input
入力:
This is an example sentence.
And this one is, too!
出力:
This is an example sentence.
And this one is, too!
注意:このプログラムは、すべての入力が完全に読み込まれる前に実際に出力の一部を出力します。つまり、たとえば、50MiBファイルに対してgetContents
を使用すると、Haskellの遅延評価とガベージコレクタは、現在必要とされているファイルの一部(次の実行には読み取り:不可欠)がメモリにロードされます。したがって、50MiBファイルは一度にメモリにロードされません。
標準入力から行を読み込む
main = do
line <- getLine
putStrLn line
入力:
This is an example.
出力:
This is an example.
標準入力からオブジェクトを解析して構築する
readFloat :: IO Float
readFloat =
fmap read getLine
main :: IO ()
main = do
putStr "Type the first number: "
first <- readFloat
putStr "Type the second number: "
second <- readFloat
putStrLn $ show first ++ " + " ++ show second ++ " = " ++ show ( first + second )
入力:
Type the first number: 9.5
Type the second number: -2.02
出力:
9.5 + -2.02 = 7.48
ファイルハンドルからの読み取り
I / Oライブラリの他のいくつかの部分と同様に、暗黙的に標準ストリームを使用する関数は、同じジョブを実行するSystem.IO
対応しますが、ストリームの種類を示すHandle
型の余分なパラメータを持ちます処理された。例えば、 getLine :: IO String
は、対応するhGetLine :: Handle -> IO String
持っています。
import System.IO( Handle, FilePath, IOMode( ReadMode ),
openFile, hGetLine, hPutStr, hClose, hIsEOF, stderr )
import Control.Monad( when )
dumpFile :: Handle -> FilePath -> Integer -> IO ()
dumpFile handle filename lineNumber = do -- show file contents line by line
end <- hIsEOF handle
when ( not end ) $ do
line <- hGetLine handle
putStrLn $ filename ++ ":" ++ show lineNumber ++ ": " ++ line
dumpFile handle filename $ lineNumber + 1
main :: IO ()
main = do
hPutStr stderr "Type a filename: "
filename <- getLine
handle <- openFile filename ReadMode
dumpFile handle filename 1
hClose handle
ファイルexample.txt
内容:
This is an example.
Hello, world!
This is another example.
入力:
Type a filename: example.txt
出力:
example.txt:1: This is an example.
example.txt:2: Hello, world!
example.txt:3: This is another example
ファイルの終わりの状態をチェックする
他のほとんどの言語の標準I / Oライブラリと同じように、直感的に反して、HaskellのisEOF
では、EOF条件をチェックする前に読み込み操作を実行する必要はありません。ランタイムはあなたのためにそれを行います。
import System.IO( isEOF )
eofTest :: Int -> IO ()
eofTest line = do
end <- isEOF
if end then
putStrLn $ "End-of-file reached at line " ++ show line ++ "."
else do
getLine
eofTest $ line + 1
main :: IO ()
main =
eofTest 1
入力:
Line #1.
Line #2.
Line #3.
出力:
End-of-file reached at line 4.
ファイル全体から単語を読む
Haskellでは、それは多くの場合、 ファイルを気にしないように理にかなっているすべてで処理し 、単に読み取りまたは書き込みファイル全体をまっすぐ、ディスクからメモリ†に、純粋な文字列データ構造を持つテキストのすべてのパーティショニング/処理を行います。これにより、IOとプログラムロジックの混在が回避され、バグの回避に大きく役立ちます。
-- | The interesting part of the program, which actually processes data
-- but doesn't do any IO!
reverseWords :: String -> [String]
reverseWords = reverse . words
-- | A simple wrapper that only fetches the data from disk, lets
-- 'reverseWords' do its job, and puts the result to stdout.
main :: IO ()
main = do
content <- readFile "loremipsum.txt"
mapM_ putStrLn $ reverseWords content
loremipsum.txt
含まれている場合
Lorem ipsum dolor sit amet,
consectetur adipiscing elit
プログラムは出力します
elit
adipiscing
consectetur
amet,
sit
dolor
ipsum
Lorem
ここで、 mapM_
はファイル内のすべての単語のリストをmapM_
、 mapM_
てそれぞれの行を別々の行にputStrLn
ます。
† これがメモリー上で無駄だと思うなら、あなたにはポイントがあります。実のところ、Haskellの怠惰は、ファイル全体を同時にメモリに置く必要があることをしばしば避けることができますが、この種の遅延IOは、独自の問題を引き起こします。パフォーマンスが重要なアプリケーションの場合、厳密に一度にファイル全体を強制的に実行することが理にかなっています。あなたがこれを行うことができますData.Textの
バージョンのData.Text
readFileの
。 readFile
IOはあなたのプログラムの `main`アクションを定義します
Haskellプログラムを実行可能にするには、 IO ()
型のmain
関数をファイルに与える必要があります。
main :: IO ()
main = putStrLn "Hello world!"
Haskellがコンパイルされると、ここでIO
データが調べられ、実行可能ファイルに変換されます。このプログラムを実行すると、 Hello world!
。
あなたがmain
以外のタイプIO a
値を持っているなら、何もしません。
other :: IO ()
other = putStrLn "I won't get printed"
main :: IO ()
main = putStrLn "Hello world!"
このプログラムをコンパイルして実行すると、最後の例と同じ効果があります。 other
のコードは無視されます。
でコードを作成するためにはother
あなたにそれを構成する必要がランタイム効果持ってmain
。最終的にmain
に構成されないIO
値は、ランタイム効果を持ちません。 2つのIO
値を順に作成するには、 do
-notationを使用できます。
other :: IO ()
other = putStrLn "I will get printed... but only at the point where I'm composed into main"
main :: IO ()
main = do
putStrLn "Hello world!"
other
このプログラムをコンパイルして実行すると、
Hello world!
I will get printed... but only at the point where I'm composed into main
操作の順序は、 other
が定義順ではなくmain
どのように構成されたかによって記述されることに注意してください。
IOの役割と目的
Haskellは純粋な言語であり、式が副作用を持つことはできません。副作用とは、式や関数が値を生成する以外に、たとえばグローバルカウンタを変更したり、標準出力に出力したりすることです。
ハスケルでは、副作用のある計算(具体的には現実世界に影響を及ぼす可能性のある計算)がIO
を使ってモデル化されています。厳密に言えば、 IO
は型を取り、型を生成する型コンストラクタです。たとえば、 IO Int
は、 Int
値を生成するI / O計算のタイプです。 IO
タイプは抽象であり、そしてために提供されるインターフェースIO
特定の不正な値(つまり、無意味タイプの関数である)IOを実行するすべての組み込み関数は、で囲まれた戻り型があることを保証することにより、存在しないことを保証するIO
。
Haskellプログラムが実行されると、 main
という名前のHaskell値で表される計算が実行されます。その型は、任意の型x
IO x
となることができます。
IO値の操作
標準ライブラリには、ファイルハンドルの読み取りや書き込みなど、汎用プログラミング言語が実行すべき典型的なIO
アクションを提供する多くの関数があります。一般的なIO
アクションは、主に次の2つの機能で作成され、結合されます。
(>>=) :: IO a -> (a -> IO b) -> IO b
(典型的にはバインドと呼ばれる)この関数は、かかるIO
アクションと返す関数IO
作用を、そして生成IO
最初した値に関数を適用した結果である動作IO
アクションを。
return :: a -> IO a
この関数は任意の値(すなわち純粋な値)をとり、IOを実行せずに与えられた値を生成するIO計算を返します。言い換えれば、これはI / O操作なしです。
頻繁に使用される追加の一般的な機能がありますが、これらはすべて上記の2つの点で記述することができます。たとえば、 (>>) :: IO a -> IO b -> IO b
は(>>=)
と似ていますが、最初のアクションの結果は無視されます。
これらの関数を使ってユーザーに挨拶する簡単なプログラム:
main :: IO ()
main =
putStrLn "What is your name?" >>
getLine >>= \name ->
putStrLn ("Hello " ++ name ++ "!")
このプログラムは、 putStrLn :: String -> IO ()
とgetLine :: IO String
も使用します。
注意:上記の特定の関数の型は、実際には与えられた型よりも一般的です(つまり、 >>=
、 >>
およびreturn
)。
IOセマンティクス
HaskellのIO
型は、命令型プログラミング言語と非常によく似ています。たとえば、 s1 ; s2
書き込むとしs1 ; s2
の文の実行を指示する命令型言語でs1
、その後、ステートメントs2
、一つは書くことができs1 >> s2
Haskellで同じことをモデル化します。
しかし、 IO
のセマンティクスは、不可欠なバックグラウンドからの予想とは少し異なります。 return
関数は制御フローを中断しません 。別のIO
アクションが順番に実行される場合、プログラムには何の影響も与えません。たとえば、 return () >> putStrLn "boom"
は "boom"を標準出力に正しく出力します。
IO
の正式なセマンティクスは、前のセクションの関数を含む単純な等式の観点から与えられます。
return x >>= f ≡ f x, ∀ f x
y >>= return ≡ return y, ∀ y
(m >>= f) >>= g ≡ m >>= (\x -> (f x >>= g)), ∀ m f g
これらの法則は、通常、それぞれ、左身分証明、権利識別、および構図と呼ばれます。それらは、機能に関してより自然に述べることができる
(>=>) :: (a -> IO b) -> (b -> IO c) -> a -> IO c
(f >=> g) x = (f x) >>= g
次のように:
return >=> f ≡ f, ∀ f
f >=> return ≡ f, ∀ f
(f >=> g) >=> h ≡ f >=> (g >=> h), ∀ f g h
レイジーIO
I / O計算を実行する関数は厳密に厳密であり、次のアクションが開始される前にアクションシーケンス内のすべての先行アクションを完了しなければならないことを意味します。通常、これは便利で期待される動作ですputStrLn "X" >> putStrLn "Y"
は "XY"を出力します。ただし、一部のライブラリ関数はI / O遅延を実行します。つまり、値を生成するために必要なI / Oアクションは、値が実際に消費されたときにのみ実行されます。そのような関数の例は、 getContents
とreadFile
です。 Lazy I / Oは、Haskellプログラムのパフォーマンスを大幅に低下させる可能性があるので、ライブラリ関数を使用するときは、どの関数が怠惰であるかを注意する必要があります。
IOとdo
表記
Haskellは、異なるIO値をより大きなIO値に組み合わせる簡単な方法を提供します。この特殊な構文はdo
notation *と呼ばれ、 >>=
、 >>
、およびreturn
関数の使用のための構文的な砂糖です。
前のセクションのプログラムは、 do
notationを使用do
2つの異なる方法で記述することができます。最初はレイアウトに影響され、2つ目はレイアウトに影響されdo
ん。
main = do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Hello " ++ name ++ "!")
main = do {
putStrLn "What is your name?" ;
name <- getLine ;
putStrLn ("Hello " ++ name ++ "!")
}
3つのプログラムは全く同じです。
*注do
記法もモナドと呼ばれるタイプのコンストラクタの広いクラスに適用されます。
「a」を「IO」から抜け出す
一般的な質問がされ、「私はの価値持っているIO a
が、私はそれに何かをしたい値を:どのように私はそれへのアクセスを得るのですか?」 a
外界からのデータ(例えば、ユーザーが入力した数字を増分するなど)を操作するにはどうすればよいですか?
要点は、純粋に得られたデータに対して純粋な関数を使用すると、その結果はまだ不純であるということです。それはユーザーが何をしたかによって異なります!タイプの値IO a
「型の値を生じる副作用演算を表しa
のみ (A)にそれを構成することによって実行することができ、」 main
および(b)コンパイルして、プログラムを実行します。そのため、「取得するための純粋なHaskellの世界の中で方法はありませんアウト」。 a
その代わりに、 実行時に a
値を使用する新しいIO
値である新しい計算を作成したいと考えています 。これはIO
値を構成するもう1つの方法であり、再びdo
-notationを使用できます。
-- assuming
myComputation :: IO Int
getMessage :: Int -> String
getMessage int = "My computation resulted in: " ++ show int
newComputation :: IO ()
newComputation = do
int <- myComputation -- we "bind" the result of myComputation to a name, 'int'
putStrLn $ getMessage int -- 'int' holds a value of type Int
ここでは、純粋な機能(使用しているgetMessage
オンにする) Int
にString
が、私たちが使っているdo
それが結果に適用させるために表記法をIO
計算myComputation
その計算の実行をするとき (後)。結果はより大きなIO
計算、 newComputation
です。不純なコンテキストで純粋な関数を使用するこのテクニックをリフティングといいます。
stdoutへの書き込み
Haskell 2010言語仕様では、Preludeで利用可能な標準的なIO関数がありますので、それらを使用するためのインポートは必要ありません。
putChar :: Char -> IO ()
- char
をstdout
書き込みます。
Prelude> putChar 'a'
aPrelude> -- Note, no new line
putStr :: String -> IO ()
- stdout
String
を書き込みます。
Prelude> putStr "This is a string!"
This is a string!Prelude> -- Note, no new line
putStrLn :: String -> IO ()
- stdout
String
を書き込み、新しい行を追加する
Prelude> putStrLn "Hi there, this is another String!"
Hi there, this is another String!
print :: Show a => a -> IO ()
-書き込みのインスタンスa
Show
にstdout
Prelude> print "hi"
"hi"
Prelude> print 1
1
Prelude> print 'a'
'a'
Prelude> print (Just 'a') -- Maybe is an instance of the `Show` type class
Just 'a'
Prelude> print Nothing
Nothing
deriving
を使用して独自のタイプのShow
をインスタンス化できることを思い出してください:
-- In ex.hs
data Person = Person { name :: String } deriving Show
main = print (Person "Alex") -- Person is an instance of `Show`, thanks to `deriving`
GHCiの読み込みと実行:
Prelude> :load ex.hs
[1 of 1] Compiling ex ( ex.hs, interpreted )
Ok, modules loaded: ex.
*Main> main -- from ex.hs
Person {name = "Alex"}
*Main>
`stdin`から読む
Haskell 2010言語仕様に従って、以下はPreludeで利用可能な標準的なIO関数であるため、それらを使用するためのインポートは必要ありません。
getChar :: IO Char
- stdin
からChar
を読み込む
-- MyChar.hs
main = do
myChar <- getChar
print myChar
-- In your shell
runhaskell MyChar.hs
a -- you enter a and press enter
'a' -- the program prints 'a'
getLine :: IO String
- stdin
からString
を読み込み、改行文字を表示しない
Prelude> getLine
Hello there! -- user enters some text and presses enter
"Hello there!"
read :: Read a => String -> a
- 文字列を値に変換する
Prelude> read "1" :: Int
1
Prelude> read "1" :: Float
1.0
Prelude> read "True" :: Bool
True
あまり一般的でない機能は、
-
getContents :: IO String
- すべてのユーザー入力を単一の文字列として返します。必要に応じて遅延読み込みされます。 -
interact :: (String -> String) -> IO ()
:(interact :: (String -> String) -> IO ()
- 引数としてString-> String型の関数をとります。標準入力デバイスからの入力全体が引数としてこの関数に渡されます