サーチ…


標準入力のすべての内容を文字列に読み込む

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アクションは、値が実際に消費されたときにのみ実行されます。そのような関数の例は、 getContentsreadFileです。 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オンにする) IntStringが、私たちが使っているdoそれが結果に適用させるために表記法をIO計算myComputationその計算の実行をするとき (後)。結果はより大きなIO計算、 newComputationです。不純なコンテキストで純粋な関数を使用するこのテクニックをリフティングといいます。

stdoutへの書き込み

Haskell 2010言語仕様では、Preludeで利用可能な標準的なIO関数がありますので、それらを使用するためのインポートは必要ありません。

putChar :: Char -> IO () - charstdout書き込みます。

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!
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型の関数をとります。標準入力デバイスからの入力全体が引数としてこの関数に渡されます


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow