Zoeken…


Alle inhoud van standaardinvoer lezen in een tekenreeks

main = do
    input <- getContents
    putStr input

Invoer:

This is an example sentence.
And this one is, too!

Output:

This is an example sentence.
And this one is, too!

Opmerking: Dit programma zal in feite voor alle ingangs- delen van de output af te drukken is volledig ingelezen Dit betekent dat, als, bijvoorbeeld, die u gebruikt. getContents over een 50MiB bestand, wordt Haskell lui evaluatie en de garbage collector dat alleen de zorgen delen van het bestand die momenteel nodig zijn (lees: onmisbaar voor verdere uitvoering) worden in het geheugen geladen. Het bestand van 50 MB wordt dus niet meteen in het geheugen geladen.

Een regel lezen van standaardinvoer

main = do
    line <- getLine
    putStrLn line

Invoer:

This is an example.

Output:

This is an example.

Een object parseren en construeren vanuit standaardinvoer

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 )

Invoer:

Type the first number: 9.5
Type the second number: -2.02

Output:

9.5 + -2.02 = 7.48

Lezen van bestandshandvatten

Net als in verschillende andere delen van de I / O-bibliotheek, hebben functies die impliciet een standaardstream gebruiken een tegenhanger in System.IO die dezelfde taak uitvoert, maar met een extra parameter aan de linkerkant, van het type Handle , die de stream vertegenwoordigt die wordt afgehandeld. getLine :: IO String heeft bijvoorbeeld een tegenhanger 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

Inhoud van het bestand example.txt :

This is an example.
Hello, world!
This is another example.

Invoer:

Type a filename: example.txt

Output:

example.txt:1: This is an example.
example.txt:2: Hello, world!
example.txt:3: This is another example

Controleren op voorwaarden voor einde bestand

Een beetje contra-intuïtief voor de manier waarop de standaard I / O-bibliotheken van de meeste andere talen dit doen, Haskell's isEOF vereist niet dat je een leesbewerking uitvoert voordat je op een EOF-toestand controleert; de runtime zal het voor u doen.

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

Invoer:

Line #1.
Line #2.
Line #3.

Output:

End-of-file reached at line 4.

Woorden uit een heel bestand lezen

In Haskell is het vaak zinvol om helemaal niet met bestandshandvatten bezig te zijn, maar gewoon een heel bestand rechtstreeks van schijf naar geheugen te lezen of te schrijven , en alle partities / verwerking van de tekst met de pure string datastructuur te doen. Dit vermijdt het mengen van IO en programmalogica, wat enorm kan helpen bugs te vermijden.

-- | 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

Als loremipsum.txt bevat

Lorem ipsum dolor sit amet,
consectetur adipiscing elit

dan wordt het programma uitgevoerd

elit
adipiscing
consectetur
amet,
sit
dolor
ipsum
Lorem

Hier ging mapM_ door de lijst met alle woorden in het bestand en drukte ze elk op een afzonderlijke regel af met putStrLn .


Als je denkt dat dit verspilling van geheugen is, heb je een punt. Haskell's luiheid kan eigenlijk vaak voorkomen dat het hele bestand tegelijkertijd in het geheugen moet worden bewaard ... maar pas op, dit soort luie IO veroorzaakt zijn eigen problemen. Voor prestatie-kritische applicaties is het vaak zinvol om het hele bestand strikt strikt te lezen. u kunt dit doen met de Data.Text versie van readFile .

IO definieert de `hoofd` actie van uw programma

Om een Haskell programma te maken uitvoerbaar u een bestand met een moet verstrekken main functie van het type IO ()

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

Wanneer Haskell wordt gecompileerd, onderzoekt het hier de IO gegevens en verandert het in een uitvoerbaar bestand. Wanneer we dit programma uitvoeren, wordt Hello world! afgedrukt Hello world! .

Als u waarden van type IO a andere dan main , zullen ze niets doen.

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

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

Dit programma compileren en uitvoeren heeft hetzelfde effect als het laatste voorbeeld. De code in other wordt genegeerd.

Om ervoor te zorgen dat de code in other runtime-effecten komt, moet u deze in main samenstellen . Geen IO waarden die uiteindelijk niet in de main zijn opgenomen, hebben een runtime-effect. Om twee IO waarden opeenvolgend samen te stellen, kunt u do -notatie gebruiken:

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

Wanneer u dit programma compileert en uitvoert , wordt het uitgevoerd

Hello world!
I will get printed... but only at the point where I'm composed into main

Merk op dat de volgorde van bewerkingen wordt beschreven door hoe other in main is samengesteld en niet de definitie-volgorde.

Rol en doel van IO

Haskell is een pure taal, wat betekent dat uitdrukkingen geen bijwerkingen kunnen hebben. Een neveneffect is alles wat de uitdrukking of functie anders doet dan een waarde produceren, bijvoorbeeld een globale teller wijzigen of afdrukken naar standaarduitvoer.

In Haskell worden neveneffectieve berekeningen (met name die welke een effect kunnen hebben op de echte wereld) gemodelleerd met behulp van IO . Strikt genomen is IO een typebouwer die een type neemt en een type produceert. IO Int is bijvoorbeeld het type I / O-berekening dat een Int waarde Int . Het IO type is abstract en de interface die wordt geboden voor IO zorgt ervoor dat bepaalde illegale waarden (dat wil zeggen functies met niet-sensuele typen) niet kunnen bestaan, door ervoor te zorgen dat alle ingebouwde functies die IO uitvoeren, een retourtype hebben dat is ingesloten in IO .

Wanneer een Haskell-programma wordt uitgevoerd, wordt de berekening uitgevoerd die wordt weergegeven door de Haskell-waarde main , waarvan het type IO x kan zijn voor elk type x .

IO-waarden manipuleren

Er zijn veel functies in de standaardbibliotheek die typische IO acties bieden die een programmeertaal voor algemene doeleinden zou moeten uitvoeren, zoals lezen en schrijven naar bestandshandvatten. Algemene IO acties worden gemaakt en voornamelijk gecombineerd met twee functies:

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

Deze functie (meestal bind genoemd ) neemt een IO actie en een functie die een IO actie retourneert, en produceert de IO actie die het resultaat is van het toepassen van de functie op de waarde die wordt geproduceerd door de eerste IO actie.

 return :: a -> IO a

Deze functie neemt elke waarde (dwz een pure waarde) en retourneert de IO-berekening die geen IO doet en de gegeven waarde produceert. Met andere woorden, het is een no-op I / O-actie.

Er zijn extra algemene functies die vaak worden gebruikt, maar ze kunnen allemaal worden beschreven in termen van de twee hierboven. (>>) :: IO a -> IO b -> IO b is vergelijkbaar met (>>=) maar het resultaat van de eerste actie wordt genegeerd.

Een eenvoudig programma dat de gebruiker begroet met behulp van deze functies:

 main :: IO ()
 main =
   putStrLn "What is your name?" >>
   getLine >>= \name ->
   putStrLn ("Hello " ++ name ++ "!")

Dit programma gebruikt ook putStrLn :: String -> IO () en getLine :: IO String .


Opmerking: de typen van bepaalde functies hierboven zijn eigenlijk algemener dan de gegeven typen (namelijk >>= , >> en return ).

IO semantiek

Het IO type in Haskell heeft zeer vergelijkbare semantiek als die van imperatieve programmeertalen. Bijvoorbeeld wanneer men s1 ; s2 schrijft s1 ; s2 in een gebiedende taal om aan te geven dat instructie s1 en vervolgens instructie s2 , kan men s1 >> s2 om hetzelfde in Haskell te modelleren.

De semantiek van IO wijkt echter enigszins af van wat verwacht zou kunnen worden vanuit een imperatieve achtergrond. De return onderbreekt de besturingsstroom niet - het heeft geen effect op het programma als een andere IO actie in volgorde wordt uitgevoerd. Bijvoorbeeld, return () >> putStrLn "boom" drukt "boom" correct af naar standaarduitvoer.


De formele semantiek van IO kan worden gegeven in termen van eenvoudige gelijkheden met betrekking tot de functies in de vorige sectie:

 return x >>= f ≡ f x, ∀ f x
 y >>= return ≡ return y, ∀ y
 (m >>= f) >>= g ≡ m >>= (\x -> (f x >>= g)), ∀ m f g

Deze wetten worden doorgaans respectievelijk linkse identiteit, rechteridentiteit en samenstelling genoemd. Ze kunnen natuurlijker worden gezegd in termen van de functie

 (>=>) :: (a -> IO b) -> (b -> IO c) -> a -> IO c
 (f >=> g) x = (f x) >>= g

als volgt:

 return >=> f ≡ f, ∀ f
 f >=> return ≡ f, ∀ f
 (f >=> g) >=> h ≡ f >=> (g >=> h), ∀ f g h

Luie IO

Functies die I / O-berekeningen uitvoeren, zijn doorgaans strikt, wat betekent dat alle voorgaande acties in een reeks acties moeten worden voltooid voordat de volgende actie wordt gestart. Meestal is dit nuttig en verwacht gedrag - putStrLn "X" >> putStrLn "Y" moet "XY" afdrukken. Bepaalde bibliotheekfuncties voeren echter lui I / O uit, wat betekent dat de I / O-acties die nodig zijn om de waarde te produceren, alleen worden uitgevoerd wanneer de waarde daadwerkelijk wordt verbruikt. Voorbeelden van dergelijke functies zijn getContents en readFile . Lazy I / O kan de prestaties van een Haskell-programma drastisch verminderen, dus als u bibliotheekfuncties gebruikt, moet u oppassen welke functies lui zijn.

IO en do notatie

Haskell biedt een eenvoudigere methode voor het combineren van verschillende IO-waarden in grotere IO-waarden. Deze speciale syntax is bekend als do notatie * en is gewoon syntactische suiker voor het gebruiken van de >>= , >> en return functies.

Het programma in de vorige paragraaf kan worden geschreven op twee verschillende manieren met behulp van do notatie, waarvan de eerste lay-out-gevoelig is en de tweede layout ongevoelig:

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


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

Alle drie programma's zijn exact gelijkwaardig.


* Merk op dat do notatie is ook van toepassing op een ruimere klasse van het type constructeurs genoemd monaden.

Haal de 'a' uit '' IO a '

Een veel voorkomende vraag is: "Ik heb een waarde van IO a , maar ik wil iets doen aan die a waarde: hoe krijg ik toegang tot deze" Hoe kan men werken met gegevens die van de buitenwereld afkomstig zijn (bijvoorbeeld een door de gebruiker getypt getal ophogen)?

Het punt is dat als u een pure functie gebruikt voor gegevens die onzuiver zijn verkregen, het resultaat nog steeds onzuiver is. Het hangt ervan af wat de gebruiker heeft gedaan! Een waarde van type IO a staat voor een "neveneffectberekening die resulteert in een waarde van type a " die alleen kan worden uitgevoerd door (a) deze samen te stellen in main en (b) uw programma te compileren en uit te voeren. Om die reden is er geen manier om binnen pure Haskell wereld om "het a out".

In plaats daarvan willen we een nieuwe berekening, een nieuw te bouwen IO waarde, die gebruik maakt van de a waarde bij runtime. Dit is een andere manier om IO waarden samen te stellen en dus kunnen we opnieuw do -notation gebruiken:

-- 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

Hier zijn we met behulp van een pure functie ( getMessage ) om een te zetten Int in een String , maar we gebruiken do notatie te maken worden toegepast op het resultaat van een IO berekening myComputation wanneer (na) die berekening runs. Het resultaat is een grotere IO berekening, newComputation . Deze techniek om pure functies in een onzuivere context te gebruiken, wordt lifting genoemd .

Schrijven naar stdout

Volgens de Haskell 2010-taalspecificatie zijn de volgende standaard IO-functies beschikbaar in Prelude, dus er is geen invoer vereist om ze te gebruiken.

putChar :: Char -> IO () - schrijft een char naar stdout

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

putStr :: String -> IO () - schrijft een String naar stdout

Prelude> putStr "This is a string!"
This is a string!Prelude>  -- Note, no new line

putStrLn :: String -> IO () - schrijft een String naar stdout en voegt een nieuwe regel toe

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

Bedenk dat u Show voor uw eigen typen kunt instantiëren met behulp van deriving :

-- In ex.hs
data Person = Person { name :: String } deriving Show
main = print (Person "Alex")  -- Person is an instance of `Show`, thanks to `deriving`

Laden en uitvoeren in 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>

Lezen van `stdin`

Volgens de Haskell 2010-taalspecificatie zijn de volgende standaard IO-functies beschikbaar in Prelude, dus er is geen invoer vereist om ze te gebruiken.

getChar :: IO Char - lees een Char van stdin

-- 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 - lees een String van stdin , zonder nieuw lijnteken

Prelude> getLine
Hello there!  -- user enters some text and presses enter
"Hello there!"

read :: Read a => String -> a - converteer een String naar een waarde

Prelude> read "1" :: Int
1
Prelude> read "1" :: Float
1.0
Prelude> read "True" :: Bool
True

Andere, minder gebruikelijke functies zijn:

  • getContents :: IO String - retourneert alle gebruikersinvoer als een enkele string, die lui wordt gelezen als dat nodig is
  • interact :: (String -> String) -> IO () - neemt een functie van het type String-> String als argument. De volledige invoer van het standaardinvoerapparaat wordt als argument doorgegeven aan deze functie


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow