수색…


표준 입력의 모든 내용을 문자열로 읽어들입니다.

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 라이브러리의 다른 여러 부분과 마찬가지로 암시 적으로 표준 스트림을 사용하는 함수는 동일한 작업을 수행하지만 왼쪽에 Handle 유형의 추가 매개 변수가있는 System.IO 의 대응 부분을 갖습니다. 처리. 예를 들어, 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

파일 끝 (end-of-file) 조건 확인

대부분의 다른 언어의 표준 I / O 라이브러리 isEOF 직관적 인 방식을 사용하기 때문에 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.

전체 파일에서 단어 읽기

하스켈에서는 종종 파일을 모두 처리하는 신경, 단순히 읽거나 바로 디스크에서 메모리 †에 전체 파일을 작성하고 순수한 문자열 데이터 구조와 텍스트의 모든 파티션 / 처리를 할 수 없습니다 의미가 있습니다. 이렇게하면 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_ 은 파일의 모든 단어 목록을 관통하고 각각을 putStrLn 과 별도의 줄에 인쇄했습니다.


이것이 메모리에서 낭비라고 생각한다면, 당신은 요점이 있습니다. 사실, Haskell의 게으름은 종종 전체 파일이 메모리에 동시에 존재해야한다는 것을 피할 수 있습니다. 그러나 이런 종류의 게으른 입출력은 그 자체의 문제를 야기합니다. 성능이 중요한 응용 프로그램의 경우 전체 파일을 한 번에 강제로 적용하는 것이 좋습니다. readFileData.Text 버전 Data.Text 작업을 수행 할 수 있습니다 .

IO는 프로그램의`main` 액션을 정의합니다

Haskell 프로그램을 실행 가능하게 만들려면 파일에 IO () 유형의 main 함수를 제공해야합니다.

main :: IO ()
main = putStrLn "Hello world!"

하스켈은 컴파일 될 때 여기에있는 IO 데이터를 검사하여 실행 파일로 만듭니다. 이 프로그램을 실행하면 Hello world! .

만약 당신이 main 이외 IO a 다른 타입의 IO a 값을 가지고 있다면 아무것도하지 않을 것입니다.

other :: IO ()
other = putStrLn "I won't get printed"

main :: IO ()
main = putStrLn "Hello world!"

이 프로그램을 컴파일하고 실행하면 마지막 예제와 동일한 효과가 있습니다. other 있는 코드는 무시됩니다.

other 클래스의 코드를 런타임 효과가 있도록하려면 main 으로 구성 해야합니다. 결국 main 으로 구성되지 않은 IO 값은 런타임 효과를 갖지 않습니다. 두 개의 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 IntInt 값을 생성하는 I / O 계산의 유형입니다. IO 유형은 추상적이며, 제공 인터페이스 IO 특정 불법 값 (즉, 비 sensical 유형 기능입니다) IO를 수행하는 모든 내장 함수가 안에 반환 형식이 보장으로 존재 할 수 없도록 IO .

하스켈 프로그램이 실행될 때 어떤 타입 x 에 대해서도 IO x 가 될 수있는 main 이라는 Haskell 값으로 표현 된 계산이 실행됩니다.

IO 값 조작

파일 핸들 읽기 및 쓰기와 같이 범용 프로그래밍 언어에서 수행해야하는 일반적인 IO 작업을 제공하는 표준 라이브러리에는 많은 기능이 있습니다. 일반적인 IO 작업은 주로 두 가지 기능으로 만들어지고 결합됩니다.

 (>>=) :: IO a -> (a -> IO b) -> IO b

(일반적으로 결합 함)이 함수는 얻어 IO 동작과 리턴하는 함수 IO 동작을하고, 생성 IO 제에 의해 생성 된 값의 함수 적용의 결과 동작 IO 행동.

 return :: a -> IO a

이 함수는 임의의 값 (즉, 순수한 값)을 취하여 IO가없고 주어진 값을 생성하는 IO 계산을 반환합니다. 즉, I / O 작업이 없습니다.

자주 사용되는 추가 일반 기능이 있지만 위의 두 가지 측면 모두로 작성 될 수 있습니다. 예를 들어, (>>) :: 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 의미

하스켈의 IO 유형은 명령형 프로그래밍 언어와 매우 유사한 의미를 갖는다. 예를 들어, s1 ; s2 명령형 언어는 문 실행 표시하기 s1 , 다음 문 s2 , 하나는 쓸 수 s1 >> s2 하스켈에서 같은 일을 모델링 할 수 있습니다.

그러나 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

이 법칙은 일반적으로 왼쪽 ID, 올바른 ID 및 구성이라고합니다. 기능면에서 더 자연스럽게 표현 될 수 있습니다.

 (>=>) :: (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

게으른 입출력

I / O 계산을 수행하는 함수는 일반적으로 엄격합니다. 즉, 다음 동작이 시작되기 전에 일련의 동작에서 모든 선행 동작을 완료해야합니다. 일반적으로 이것은 유용하고 예상되는 동작입니다. putStrLn "X" >> putStrLn "Y" 는 "XY"를 출력해야합니다. 그러나 특정 라이브러리 함수는 lazily I / O를 수행합니다. 즉, 값을 생성하는 데 필요한 I / O 작업은 값이 실제로 소비되었을 때만 수행됩니다. 이러한 함수의 예는 getContentsreadFile 입니다. Lazy I / O는 Haskell 프로그램의 성능을 크게 떨어 뜨릴 수 있으므로 라이브러리 함수를 사용할 때는 어떤 함수가 게으른 지주의해야합니다.

IO 및 do 표기

Haskell은 다른 IO 값을 더 큰 IO 값으로 결합하는 더 간단한 방법을 제공합니다. 이 특수 구문은 do notation *으로 알려져 있으며 >>= , >>return 함수의 사용법에 대한 단순한 구문 설탕입니다.

이전 섹션의 프로그램은 do notation을 사용하여 두 가지 방법으로 작성 될 수 있습니다. 첫 번째는 레이아웃에 민감하고 두 번째는 레이아웃에 영향을받지 않습니다.

 main = do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Hello " ++ name ++ "!")


 main = do {
   putStrLn "What is your name?" ;
   name <- getLine ;
   putStrLn ("Hello " ++ name ++ "!")
   }

세 가지 프로그램 모두 정확히 동일합니다.


*주의 do 표기법은 모나드라는 형식 생성자의 광범위한 클래스에 적용 할 수있다.

'a'를 "IO"에서 "빠져 나오십시오"

일반적인 질문은 "나는의 값이입니다 IO a , 그러나 나는 그에게 무언가를 할 값? : 나는 그것을 액세스 권한을 얻는 방법을" a 외부 세계에서 오는 데이터 (예 : 사용자가 입력 한 숫자를 증가시키는 데이터)에서 어떻게 작동 할 수 있습니까?

요점은 불순물로 얻은 데이터에 대해 순수 함수를 사용하면 그 결과는 여전히 불투명하다는 것입니다. 그것은 사용자가 한 일에 달려 있습니다! 유형의 값 IO a "유형의 값에 나타나는 사이드 행하는 연산 용 스탠드 (a)로를 구성하여 실행할 수있는" a main 및 (b)의 컴파일 및 실행 프로그램. 이러한 이유로, "를 얻을 수있는 순수한 하스켈 세계 내에서 방법이 없습니다 아웃". a

대신, 우리는 새로운 계산, 새로 구축하려는 IO 의 사용하게 값, 런타임에 값을. a 이것은 IO 값을 구성하는 또 다른 방법이며, 다시 우리는 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 으로 변환하지만, 계산을 실행할 (이후) IO 계산 myComputation 의 결과에 적용되도록 do notation을 사용합니다. 결과는 더 큰 IO 계산, newComputation 입니다. 순수하지 않은 컨텍스트에서 순수한 함수를 사용하는 이러한 기술을 리프팅 이라고합니다.

stdout에 쓰기

Haskell 2010 언어 사양에 따라 다음은 Prelude에서 사용할 수있는 표준 IO 기능이므로 사용하기 위해 가져 오기가 필요하지 않습니다.

putChar :: Char -> IO () - charstdout 씁니다.

Prelude> putChar 'a'
aPrelude>  -- Note, no new line

putStr :: String -> IO () - Stringstdout 씁니다.

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 () - 인수로 String -> String 유형의 함수를 사용합니다. 표준 입력 장치의 전체 입력은 인수로이 함수에 전달됩니다.


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow