Haskell Hero

Interaktivní učebnice pro začínající Haskellisty

Vstup a výstup I

Proč vstupní a výstupní operace?

Zatím jsme si ukázali, jak vyhodnocovat výrazy a jak definovat funkce. Mnohdy ale budeme od našich programů chtít, aby:

  • načítaly vstup od uživatele
  • načítaly vstup ze souborů
  • informovaly uživatele o postupu výpočtu
  • ...

K těmto úkonům slouží vstupní a výstupní operace běžně označované jako IO-operace (z anglického input/output) nebo prostě IO.

Tyto operace se také nazývají akce.

Základní operace

Pokud si necháme vyhodnotit výraz "bbbccc", Hugs zjistí, že jde o výraz a vypíše nám jej v nezměněné podobě. Pokud si ale necháme vyhodnotit výraz

putStr "bbbccc"
Hugs zjistí, že putStr je akce, kterou následně provede. Akce putStr dělá to, že na obrazovku vypíše řetězec, který jí dáme v argumentu. Při provádění akce se nevypisuje výsledek. Vypsání na obrazovku je pouze efektem akce putStr.

Funkce putStr navíc umí zobrazovat i speciální znaky. Například vykonání akce

putStr "aaa\nbbb\nccc"
dopadne tak, že \n se nahradí odřádkováním a na obrazovku se vypíše
aaa
bbb
ccc

Zmiňme některé další jednoduché IO akce:

  • putStrLn to samé, co putStr, ale po vypsání argumentu navíc odřádkuje
  • getLine počká na vstup od uživatele, jehož hodnotu poté vrátí
  • readFile načte obsah souboru

Řetězení operací

Většinou nebudeme chtít provést jen jednu IO operaci, ale budeme jich chtít za sebe poskládat více. Například budeme chtít načíst nějakou hodnotu od uživatele, tu zpracovat a vypsat uživateli výsledek.

K řetězení operací se používá konstrukce do. Například nulární funkce vypis, která vypíše řetězce "abc", "def" a "ghi" bude definována následovně:

vypis = do putStrLn "abc"
           putStrLn "def"
           putStrLn "ghi"
Pozor na zarovnání! Opět platí, že příkazy, které se mají v do-bloku provést, musí být zarovnány pod sebe. Existuje i varianta zápisu na jeden řádek:
vypis = do {putStrLn "abc"; putStrLn "def"; putStrLn "ghi"}

Mimo samotné operace si v do-bloku můžeme "uložit" výsledek vstupní operace a dále pracovat jen s odkazem na něj.

názevOdkazu <- vstupníOperace

"Uložit" je v uvozovkách, protože se nejedná přímo o zapsání dat na nějaké místo v paměti. Proč tomu tak je si povíme později.

Ukládání mezivýsledku můžeme provádět pouze v do-bloku. Pokud jej použijeme mimo do-blok, překlad skončí chybou.

Příklad

Definujte nulární funkci main :: IO (), která získá od uživatele řetězec, vypíše na výstup Napsal jsi: a poté vypíše získaný vstup.


Definici zapíšeme přesně tak, jak nám říká zadání:

  1. Získáme od uživatele vstup, který si uložíme.
  2. Vypíšeme na obrazovku řetězec "Napsal jsi: ".
  3. Vypíšeme získaný řetězec, který jsme si uložili na začátku.
main = do s <- getLine
          putStr "Napsal jsi: "
          putStr s