Interakcja z użytkownikiem i IO

Kompilacja programów i monada IO

Programy w języku Haskell można kompilowac do postaci uruchamialnej, niewymagajacej interpretera. Polcenie do kompilacji:

ghc -o executable_name source_file.hs

Kod, który ma się wykonać po uruchomieniu programu wrzucamy do funkcji main:

main :: IO ()
main = putStrLn "Hello, World!"

I kompilacja:

ghc -o hello hello.hs
./hello

Zwróć uwagę na nagłówek funkcji main, która jest typu IO i zwraca () – coś na wzór void. IO to specjalna wbudowana monada (podobnie jak Maybe) Funkcja main jest tak naprawdę akcją IO, czyli komponentem programu zintegrowanym ze światem zewnętrznym, który może potencjalnie mieć wpływ na zmianę stanu tego świata zewnętrznego.

Analogicznie funkcja wczytująca napis z klawiatury, będzie akcją zdefiniowaną jako:

getLine' :: IO String

Czyli, jest akcją typu IO i zwraca String.

Interakcja z użytkownikiem

Przykład najprostszej interakcji z użytkownikiem:

main = do
    putStrLn "Hello, what's your name?"
    name <- getLine
    putStrLn $ "Hello " ++ name ++ " good to see you!"

Spotykamy tutaj nową rzecz: do. Nie jest to jednak żaden nowy operator, tylko syntaktyczny sugar dla łączenia akcji w ciąg o ustalonej kolejności przy pomocy operatora `»=` i `»` Uwaga Każda z linii w bloku do musi być IO akcją.

Na przykład alternatywnym zapisem dla powyższego z wykorzystaniem operatorów `»` i `»=` jest kod poniżej:

main =   putStrLn "Hello, what's your name?" >> (  getLine  >>= (\ name ->  putStrLn $ "Hello " ++ name ++ " good to see you!"))

Uwaga na return

Spójrz na kod poniżej i zastanów się, co powinno się wydarzyć. Następwnie skompiluj, uruchom i zweryfikuj swoje podejrzenia.

main = do
    putStrLn "Hello, what's your name?"
    return ()
    name <- getLine
    putStrLn $ "Hello " ++ name ++ " good to see you!"

Polecenie return w bloku do nie kończy wcale wykonywania kodu i nie powoduje zwrócenia wartości na zewnątrz bloku. Jest ono traktowane jak kolejna akcja, która w przypadku powyżej nie robi nic.

Pliki

Czytanie z plików pokazano na przykładzie poniżej (zakładajac, ze istnieje jakiś plik 'machine.txt', np:

machine.txt
Welcome my son, welcome to the machine
Where have you been?
It's alright we know where you've been
You've been in the pipeline, filling in time
Provided with toys and 'scouting for boys'
You brought a guitar to punish your ma
And you didn't like school, and you
Know you're nobody's fool
So welcome to the machine
Welcome my son, welcome to the machine
What did you dream?
It's alright we told you what to dream
You dreamed of a big star
He played a mean guitar
He always ate in the Steak Bar
He loved to drive in his Jaguar
So welcome to the machine
import System.IO  
 
main = do  
    handle <- openFile "machine.txt" ReadMode  
    contents <- hGetContents handle  
    putStr contents  
    hClose handle

Jak widzisz uzyskanie uchwytu do pliku odbywa się za pomocą funkcji `openFile`, o sygnaturze:

openFile :: FilePath -> IOMode -> IO Handle

Gdzie `FilePath` to po prostu datatype będący synonimem Stringa, IOMode to typ opisany jak poniżej:

data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode  

Zwracana jest natomiast akcja IO będąca uchwytem do pliku.

Funkcja `hGetContents` realizuje lazy IO, czyli nie wczytuje całego pliku do pamięci, tylko czyta go kiedy jest to potrzebne. Istnieje też wersja do realizowania lazy IO w terminalu (poczytaj o getContents). W przypadku, jeśli chcemy wczytać całośc pliku do pamięci, bez lazy IO, możemy użyc `readFile`:

import System.IO  
 
main = do  
    contents <- readFile "machine.txt"  
    putStr contents

Pisanie do pliku jest wykonywane w analogiczny sposób:

import System.IO     
 
main = do     
    contents <- readFile "machine.txt"     
    writeFile "new_machine.txt" contents 

Zadania

  1. Napisz grę „Guess a number”, gdzie system losuje losową liczbę od 0-10, a użytkownik ma 3 próby odgadnięcia tej liczby, za każdym razem otrzymując podpowiedź, że wpisana liczba była mniejsza, lub większa od szukanej. Aby wygenerować losową liczbę, użyj:
    import System.Random
     
    main = do
        num <- randomIO :: IO Int
        print num
  2. Napisz program odwracający i wyświetlający napis, który wcześniej został podany przez użytkownika. Program powinien wczytywać i odwracać stringi do czasu, aż użytkownik poda pustą linię.
  3. Napisz program wczytujący plik i zapisujący jego zawartość w wersji CAPSLOCK
  4. Napisz program czytający plik i zapisujący w osobnym pliku słowa, które nie występują w '/usr/share/dict/words'. Sprawdzanie nie powinno być case-sensitive.
pl/dydaktyka/pp/haskell/lab-io.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0