learnxinyminutes-docs/pl-pl/haskell-pl.html.markdown

448 lines
13 KiB
Haskell
Raw Normal View History

2017-01-10 11:45:11 +03:00
---
language: Haskell
lang: pl-pl
contributors:
- ["Remigiusz Suwalski", "https://github.com/remigiusz-suwalski"]
---
Haskell został zaprojektowany jako praktyczy, czysto funkcyjny język
programowania. Jest znany przede wszystkim ze względu na jego monady oraz system
typów, ale ja lubię do niego wracać przez jego elegancję. Sprawił on, że
programowanie jest prawdziwą przyjemnością.
```haskell
-- Komentarze jednolinijkowe zaczynają się od dwóch myślników
{- Komentarze wielolinijkowe należy
zamykać w bloki klamrami.
-}
----------------------------------------------------
2017-01-10 12:04:14 +03:00
-- 1. Podstawowe typy danych oraz operatory
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 12:04:14 +03:00
-- Mamy liczby
2017-01-10 11:45:11 +03:00
3 -- 3
2017-01-10 12:04:14 +03:00
-- Podstawowe działania działają tak, jak powinny
2017-01-10 11:45:11 +03:00
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
35 / 5 -- 7.0
2017-01-10 12:04:14 +03:00
-- dzielenie domyślnie zwraca ,,dokładny'' wynik
2017-01-10 11:45:11 +03:00
35 / 4 -- 8.75
2017-01-10 12:04:14 +03:00
-- dzielenie całkowitoliczbowe
2017-01-10 11:45:11 +03:00
35 `div` 4 -- 8
2017-01-10 12:04:14 +03:00
-- wartości logiczne także są podstawowym typem danych:
2017-01-10 11:45:11 +03:00
True
False
2017-01-10 12:04:14 +03:00
-- operacje logiczne: negacja oraz porównania
2017-01-10 11:45:11 +03:00
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True
2017-01-10 12:04:14 +03:00
-- W powyższych przykładach, `not` jest funkcją przyjmującą jeden argument.
-- Haskell nie potrzebuje nawiasów, by wywołać funkcję: argumenty są po prostu
-- wypisywane jeden za drugim. Ogólnie wygląda to tak:
-- funkcja arg1 arg2 arg3...
-- Sekcja poświęcona funkcjom zawiera informacje, jak stworzyć własne.
2017-01-10 11:45:11 +03:00
2017-01-10 12:04:14 +03:00
-- Łańcuchy znaków (stringi) i pojedyncze znaki:
"To jest lancuch."
'a' -- znak
'Nie mozna laczyc apostrofow z lancuchami.' -- błąd!
2017-01-10 11:45:11 +03:00
2017-01-10 12:04:14 +03:00
-- Łańcuchy można sklejać
2017-01-10 11:45:11 +03:00
"Hello " ++ "world!" -- "Hello world!"
2017-01-10 12:04:14 +03:00
-- Łańcuch jest listą własnych znaków
2017-01-10 11:45:11 +03:00
['H', 'e', 'l', 'l', 'o'] -- "Hello"
2017-01-10 12:04:14 +03:00
"To jest lancuch" !! 0 -- 'T'
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 11:58:12 +03:00
-- Listy oraz krotki
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 11:58:12 +03:00
-- Wszystkie elementy listy muszą być tego samego typu.
-- Poniższe dwie listy są identyczne:
2017-01-10 11:45:11 +03:00
[1, 2, 3, 4, 5]
[1..5]
2017-01-10 11:58:12 +03:00
-- Zakresy są uniwersalne.
2017-01-10 11:45:11 +03:00
['A'..'F'] -- "ABCDEF"
2017-01-10 11:58:12 +03:00
-- Przy tworzeniu zakresów można określić krok.
2017-01-10 11:45:11 +03:00
[0,2..10] -- [0, 2, 4, 6, 8, 10]
2017-01-10 11:58:12 +03:00
[5..1] -- To nie zadziała, gdyż w Haskellu zakresy tworzone są domyślnie rosnąco
2017-01-10 11:45:11 +03:00
[5,4..1] -- [5, 4, 3, 2, 1]
2017-01-10 11:58:12 +03:00
-- indeksowanie listy od zera
2017-01-10 11:45:11 +03:00
[1..10] !! 3 -- 4
2017-01-10 11:58:12 +03:00
-- Można nawet tworzyć listy nieskończone!
[1..] -- lista wszystkich liczb naturalnych
2017-01-10 11:45:11 +03:00
2017-01-10 11:58:12 +03:00
-- Nieskończone listy mają prawo działać, ponieważ Haskell cechuje się leniwym
-- wartościowaniem. To oznacza, że obliczane są jedynie te elementy listy,
-- których istotnie potrzebujemy. Możemy poprosić o tysiączny element i
-- dostaniemy go:
2017-01-10 11:45:11 +03:00
[1..] !! 999 -- 1000
2017-01-10 11:58:12 +03:00
-- Haskell wyznaczył pierwsze tysiąc elementów listy, ale cała jej reszta
-- jeszcze nie istnieje! Nie zostanie obliczona ich wartość, póki nie zajdzie
-- taka potrzeba.
2017-01-10 11:45:11 +03:00
2017-01-10 11:58:12 +03:00
-- łączenie dwóch list
2017-01-10 11:45:11 +03:00
[1..5] ++ [6..10]
2017-01-10 11:58:12 +03:00
-- dodawanie pojedynczego elementu na początek listy
2017-01-10 11:45:11 +03:00
0:[1..5] -- [0, 1, 2, 3, 4, 5]
2017-01-10 11:58:12 +03:00
-- więcej operacji na listach
2017-01-10 11:45:11 +03:00
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5
-- list comprehensions
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
2017-01-10 11:58:12 +03:00
-- z dodatkowym warunkiem
2017-01-10 11:45:11 +03:00
[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10]
2017-01-10 11:58:12 +03:00
-- każdy element krotki może być innego typu, jednak sama krotka musi być stałej
-- długości. Przykładowo:
2017-01-10 11:45:11 +03:00
("haskell", 1)
2017-01-10 11:58:12 +03:00
-- dostęp do elementów pary (krotki długości 2):
2017-01-10 11:45:11 +03:00
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1
----------------------------------------------------
-- 3. Functions
----------------------------------------------------
-- A simple function that takes two variables
add a b = a + b
-- Note that if you are using ghci (the Haskell interpreter)
-- You'll need to use `let`, i.e.
-- let add a b = a + b
-- Using the function
add 1 2 -- 3
-- You can also put the function name between the two arguments
-- with backticks:
1 `add` 2 -- 3
-- You can also define functions that have no letters! This lets
-- you define your own operators! Here's an operator that does
-- integer division
(//) a b = a `div` b
35 // 4 -- 8
-- Guards: an easy way to do branching in functions
fib x
| x < 2 = 1
| otherwise = fib (x - 1) + fib (x - 2)
-- Pattern matching is similar. Here we have given three different
-- definitions for fib. Haskell will automatically call the first
-- function that matches the pattern of the value.
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)
-- Pattern matching on tuples:
foo (x, y) = (x + 1, y + 2)
-- Pattern matching on lists. Here `x` is the first element
-- in the list, and `xs` is the rest of the list. We can write
-- our own map function:
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)
-- Anonymous functions are created with a backslash followed by
-- all the arguments.
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
-- using fold (called `inject` in some languages) with an anonymous
-- function. foldl1 means fold left, and use the first value in the
-- list as the initial value for the accumulator.
foldl1 (\acc x -> acc + x) [1..5] -- 15
----------------------------------------------------
-- 4. More functions
----------------------------------------------------
-- partial application: if you don't pass in all the arguments to a function,
-- it gets "partially applied". That means it returns a function that takes the
-- rest of the arguments.
add a b = a + b
foo = add 10 -- foo is now a function that takes a number and adds 10 to it
foo 5 -- 15
-- Another way to write the same thing
foo = (10+)
foo 5 -- 15
-- function composition
-- the operator `.` chains functions together.
-- For example, here foo is a function that takes a value. It adds 10 to it,
-- multiplies the result of that by 4, and then returns the final value.
foo = (4*) . (10+)
-- 4*(10 + 5) = 60
foo 5 -- 60
-- fixing precedence
-- Haskell has another operator called `$`. This operator applies a function
-- to a given parameter. In contrast to standard function application, which
-- has highest possible priority of 10 and is left-associative, the `$` operator
-- has priority of 0 and is right-associative. Such a low priority means that
-- the expression on its right is applied as the parameter to the function on its left.
-- before
even (fib 7) -- false
-- equivalently
even $ fib 7 -- false
-- composing functions
even . fib $ 7 -- false
----------------------------------------------------
2017-01-10 12:10:04 +03:00
-- 5. Sygnatury typów
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 12:10:04 +03:00
-- Haskell posiada wyjątkowo silny system typów, w którym każde poprawne
-- wyrażenie ma swój typ.
2017-01-10 11:45:11 +03:00
2017-01-10 12:10:04 +03:00
-- Kilka podstawowych typów:
2017-01-10 11:45:11 +03:00
5 :: Integer
"hello" :: String
True :: Bool
2017-01-10 12:10:04 +03:00
-- Funkcje też są określonego typu.
-- `not` przyjmuje wartość logiczną i taką też zwraca:
2017-01-10 11:45:11 +03:00
-- not :: Bool -> Bool
2017-01-10 12:10:04 +03:00
-- Przykład funkcji przyjmującej dwa argumenty
2017-01-10 11:45:11 +03:00
-- add :: Integer -> Integer -> Integer
2017-01-10 12:10:04 +03:00
-- Dobrą praktyką podczas definiowania wartości jest napisanie nad nią
-- także jej typu:
2017-01-10 11:45:11 +03:00
double :: Integer -> Integer
double x = x * 2
----------------------------------------------------
2017-01-10 20:29:33 +03:00
-- 6. Wyrażenia warunkowe
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 20:29:33 +03:00
-- wyrażenie warunkowe
haskell = if 1 == 1 then "wspaniale" else "paskudnie" -- haskell = "wspaniale"
2017-01-10 11:45:11 +03:00
2017-01-10 20:29:33 +03:00
-- wyrażenie warunkowe można rozbić na wiele linii,
-- ale trzeba uważać na wcięcia w kodzie
2017-01-10 11:45:11 +03:00
haskell = if 1 == 1
2017-01-10 20:29:33 +03:00
then "wspaniale"
else "paskudnie"
2017-01-10 11:45:11 +03:00
2017-01-10 20:29:33 +03:00
-- rozpatrywanie przypadków: oto jak można parsować argumenty z linii poleceń:
2017-01-10 11:45:11 +03:00
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "bad args"
2017-01-10 20:29:33 +03:00
-- Haskell zastępuje pętle (których nie ma) rekurencyjnymi wywołaniami funkcji.
-- map aplikuje funkcję do każdego elementu listy:
2017-01-10 11:45:11 +03:00
map (*2) [1..5] -- [2, 4, 6, 8, 10]
2017-01-10 20:29:33 +03:00
-- możesz zdefiniować funkcję for przy użyciu map:
2017-01-10 11:45:11 +03:00
for array func = map func array
2017-01-10 20:29:33 +03:00
-- a następnie użyć jej:
2017-01-10 11:45:11 +03:00
for [0..5] $ \i -> show i
2017-01-10 20:29:33 +03:00
-- mogliśmy użyć krótszego zapisu bez zmiany działania funkcji for:
2017-01-10 11:45:11 +03:00
for [0..5] show
2017-01-10 20:29:33 +03:00
-- Do redukcji listy służy polecenie foldl (foldr):
2017-01-10 11:45:11 +03:00
-- foldl <fn> <initial value> <list>
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
2017-01-10 20:29:33 +03:00
-- Jest to równoważne z:
2017-01-10 11:45:11 +03:00
(2 * (2 * (2 * 4 + 1) + 2) + 3)
2017-01-10 20:29:33 +03:00
-- foldl składa od od lewej strony, foldr od prawej
2017-01-10 11:45:11 +03:00
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
2017-01-10 20:29:33 +03:00
-- To zaś równoważne jest:
2017-01-10 11:45:11 +03:00
(2 * 1 + (2 * 2 + (2 * 3 + 4)))
----------------------------------------------------
2017-01-10 12:06:27 +03:00
-- 7. Typy danych
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-10 12:06:27 +03:00
-- Oto jak tworzy się nowe typy danych w Haskellu:
2017-01-10 11:45:11 +03:00
data Color = Red | Blue | Green
2017-01-10 12:06:27 +03:00
-- Teraz można używać ich we własnych funkcjach:
2017-01-10 11:45:11 +03:00
say :: Color -> String
say Red = "You are Red!"
say Blue = "You are Blue!"
say Green = "You are Green!"
2017-01-10 12:06:27 +03:00
-- Twoje typy danych mogą posiadać nawet parametry:
2017-01-10 11:45:11 +03:00
data Maybe a = Nothing | Just a
2017-01-10 12:06:27 +03:00
-- Wszystkie poniższe są typu Maybe
Just "hello" -- typu `Maybe String`
Just 1 -- typu `Maybe Int`
Nothing -- typu `Maybe a` for any `a`
2017-01-10 11:45:11 +03:00
----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------
2017-01-13 12:10:10 +03:00
-- Chociaż obsługa wejścia i wyjścia nie może zostać wyjaśniona przez poznaniem
-- monad, spróbujemy zrobić to częściowo
2017-01-10 11:45:11 +03:00
2017-01-13 12:10:10 +03:00
-- Wykonanie programu napisanego w Haskellu wywołuje funkcję `main`
-- Musi zwrócić wartość typu `IO a` dla pewnego `a`. Przykład:
2017-01-10 11:45:11 +03:00
main :: IO ()
main = putStrLn $ "Hello, sky! " ++ (say Blue)
-- putStrLn has type String -> IO ()
2017-01-13 12:10:10 +03:00
-- Najłatwiej obsłużyć wejście i wyjście, kiedy program zostanie
-- zaimplementowany jako funkcja String -> String. Funkcja
2017-01-10 11:45:11 +03:00
-- interact :: (String -> String) -> IO ()
2017-01-13 12:10:10 +03:00
-- pobiera pewien tekst, wykonuje na nim operacje, po czym wypisuje wynik.
2017-01-10 11:45:11 +03:00
countLines :: String -> String
countLines = show . length . lines
main' = interact countLines
2017-01-13 12:10:10 +03:00
-- Możesz myśleć o wartości typu `IO ()` jako reprezentującej ciąg czynności,
-- które komputer ma wykonać, zupełnie niczym program komputerowy w imperatywnym
-- języku programowania. Akcje można łączyć przy użyciu notacji `do`:
2017-01-10 11:45:11 +03:00
sayHello :: IO ()
sayHello = do
putStrLn "What is your name?"
name <- getLine -- this gets a line and gives it the name "name"
putStrLn $ "Hello, " ++ name
2017-01-13 12:10:10 +03:00
-- Ćwiczenie: napisz własną wersję `interact`,
-- która czyta tylko jedną linię wejścia.
2017-01-10 11:45:11 +03:00
2017-01-13 12:10:10 +03:00
-- Kod w `sayHello` nigdy się nie wykona. Jedyną akcją, która zostanie
-- uruchomiona, jest wartość `main`.
-- Aby uruchomić `sayHello`, należy zastąpić poprzednią definicję `main` przez
2017-01-10 11:45:11 +03:00
-- main = sayHello
2017-01-13 12:10:10 +03:00
-- Spróbujmy lepiej zrozumieć, jak działa funkcja `getLine`, której właśnie
-- użyliśmy. Jej typem jest
2017-01-10 11:45:11 +03:00
-- getLine :: IO String
2017-01-13 12:10:10 +03:00
-- Możesz myśleć o wartości typu `IO a` jako reprezentującej program, który
-- wygeneruje wartość typu `a`, poza wszystkim innym, co jeszcze zrobi.
-- Możemy także tworzyć własne akcje typu `IO String`:
2017-01-10 11:45:11 +03:00
action :: IO String
action = do
putStrLn "This is a line. Duh"
input1 <- getLine
input2 <- getLine
-- The type of the `do` statement is that of its last line.
-- `return` is not a keyword, but merely a function
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
2017-01-13 12:10:10 +03:00
-- Możemy użyć tego tak jak używaliśmy `getLine`:
2017-01-10 11:45:11 +03:00
main'' = do
putStrLn "I will echo two lines!"
result <- action
putStrLn result
putStrLn "This was all, folks!"
2017-01-13 12:10:10 +03:00
-- Typ `IO` jest przykładem monady. Sposób w jakim Haskell używa monad do
-- obsługi wejścia i wyjścia pozwala mu być czysto funkcyjnym językiem.
-- Każda funkcja, która wchodzi w interakcje ze światem zewnętrznym, oznaczana
-- jest jako `IO` w jej sygnaturze typu, co umożliwia odróżnianie funkcji
-- czystych od zależnych od świata lub modyfikujących stan.
2017-01-10 11:45:11 +03:00
2017-01-13 12:10:10 +03:00
-- To naprawdę użyteczna własność, dzięki której jesteśmy w stanie uruchamiać
-- czyste funkcje jednocześnie.
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-11 12:21:38 +03:00
-- 9. Interaktywne środowisko programowania
2017-01-10 11:45:11 +03:00
----------------------------------------------------
2017-01-11 12:21:38 +03:00
-- Aby uruchomić repl (read-eval-print loop, interaktywne środowisko), należy
-- wpisać `ghci`. Można już programować. Do definiowania nowych wartości służy
-- słowo kluczowe `let`:
2017-01-10 11:45:11 +03:00
let foo = 5
2017-01-11 12:21:38 +03:00
-- Do sprawdzania typów dowolnej wartości (wyrażenia) wykorzystuje się `:t`:
2017-01-10 11:45:11 +03:00
> :t foo
foo :: Integer
2017-01-11 12:21:38 +03:00
-- Działania takie jak `+`, `:` czy `$`, są funkcjami.
-- Przed sprawdzeniem ich typu należy otoczyć je nawiasami:
2017-01-10 11:45:11 +03:00
> :t (:)
(:) :: a -> [a] -> [a]
2017-01-11 12:21:38 +03:00
-- Dodatkowych informacji dostarcza `:i`:
2017-01-10 11:45:11 +03:00
> :i (+)
class Num a where
(+) :: a -> a -> a
...
-- Defined in GHC.Num
infixl 6 +
2017-01-11 12:21:38 +03:00
-- Można nawet wykonywać akcje typu `IO ()`!
2017-01-10 11:45:11 +03:00
> sayHello
What is your name?
Friend!
Hello, Friend!
```
2017-01-11 12:21:38 +03:00
Pominęliśmy wiele aspektów Haskella, wliczając w to monady. To właśnie one
sprawiają, że programowanie w Haskellu sprawia tyle frajdy. Na zakończenie
pokażę Tobie implementację algorytmu quicksort w Haskellu:
2017-01-10 11:45:11 +03:00
```haskell
qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
where lesser = filter (< p) xs
greater = filter (>= p) xs
```
2017-01-11 12:21:38 +03:00
Haskell może zostać zainstalowany na co najmniej dwa sposoby:
- tradycyjnie [przy użyciu Cabala](http://www.haskell.org/platform/),
- nowocześnie [z pomocą Stack](https://www.stackage.org/install).
2017-01-10 11:45:11 +03:00
2017-01-11 12:21:38 +03:00
Godnymi poleceniami wprowadzeniami wspaniałe
[Learn you a Haskell](http://learnyouahaskell.com/) albo
2017-01-10 11:45:11 +03:00
[Real World Haskell](http://book.realworldhaskell.org/).