1. diel - Úvod do funkcionálního programovania
V tomto tutoriále si ukážeme, čo je to funkcionálne programovanie, k čomu je dobré a ako ho plne využijeme.
Funkcionálne programovanie
Funkcionálne programovanie sa hodí najmä pre prácu s dátami, napr. Na štatistiky alebo analýzy a to je tiež doména funkcionálnych jazykov. Pre ich uplatnenie je dnes pomerne veľký trh, napr. Pre finančné spoločnosti (možno ste počuli pojem Big dáta). Často sa používajú aj na machine learning alebo spracovanie prirodzeného jazyka. Výrazne zjednodušujú paralelizmus, čo je jeden z dôvodov jeho úspechu. Pre tvorbu klasických programov sa ale spravidla funkcionálne programovanie mieša s "klasickým" záväznými programovaním.
Programovacie paradigmy
Aby sme funkcionálne programovanie pochopili čo najlepšie, pripomeňme si najprv ako funguje "klasické" programovanie a spomeňme si aj ďalšie konkurenčné programátorské paradigma.
Imperatívne programovanie
Najskôr sa ale pozrime, ako sa programuje "normálne". Jazyky ako C, Java, Python, C # a ďalšie sú skvelými ukážkami tzv. Imperatívny spôsobu programovania. V podstate hovoríme programe, čo má robiť. Ukážme si nejaký jednoduchý kód:
int [] pole = {1, 2, 3, 4, 5}; string spocti(int [] a) { int b = a.length; int c = b – 2; System.out.println(c - b); string s = "Hurrraaaaaaaaa, je to v kapse"; return s; }
Do premenné a
si uložíme pole prvkov, ktoré funkcie dostane
ako parameter. Do b
si uložíme dĺžku a
, do
c
si dosadíme b - 2
, a potom vytlačíme výsledok.
Parameter a
značí objekt, ktorý má nejaké vlastnosti a jedným
z nich je dĺžka. Opýtania sa na dĺžku je konštantná operácie, objekt, tu
pole, si samo pamätá, ako je veľké, lebo ho vopred staticky inicializujeme
čiže vopred počítaču povieme, ako ho má pripraviť.
Zatiaľ asi nič nové.
Deklaratívne programovanie
Pre určité typy úloh sa ale môže hodiť iný prístup. Tých máme dokonca viac. Jedným z nich je povedať počítači, ako vyzerá náš problém. Tomu sa hovorí deklaratívne programovanie, alebo niekedy tiež "logické programovanie". Proste popíšeme aký chceme výsledok, nie postup na jeho dosiahnutie, ten je už na počítači.
Ukážkou deklaratívneho jazyka je populárny SQL, kde nehovoríme ako má databázy interne dáta presne získať, ale len špecifikujeme aké dáta nás vo výsledku zaujímajú, ako majú byť zoradené, aké majú obsahovať stĺpce a podobne. Databáza to za nás potom už vymyslí a vnútorne našu požiadavku preloží na nejaké imperatívne cykly a podmienky. Od tých sme my ale plne odtienenie.
Deklaratívne programovanie však nie je obmedzené len na databázy. Najlepším nástrojom pre ukážku logického programovania je programovací jazyk Prolog, ktorého meno pochádza z Programing in logike. Logické programovanie pochádza z matematickej logiky, kedy z nejakého stavu možno jasne povedať, či platia alebo nie. A pokiaľ sa nedá dokázať, že platí, potom neplatí. Ako ukážku použijeme rodokmeň. Ten budeme brať ako nejakú databázu faktov az nich chceme získať výsledok:
matka(jana,tereza). matka(jana,jitka). matka(jitka,anna). otec(jan,jitka). otec(jiri,anna). babicka(Kdo, Koho):-matka(Kdo,X), ( otec(X,Koho) ; matka(X,Koho) ).
Je jasné, že kód sa na prvý pohľad zdá len ako mágia, ale hneď to
snáď bude jasnejšie. (;)
A (,)
sú symboly z
logiky, kde (,)
znamená and
(a zároveň) a
(;)
označuje or
(alebo). Zátvorky si predstavte ako
čiernu krabičku ( "tu strčíme niečo dovnútra, tu vylezie výsledok"). Ak
"strčíme" do zátvorky meno, program nám povie, či je niekto babičkou
niekoho v tom danom systéme. Ak máme v databáze informácie
o tom, že je Jána matka Jitky a Jitka matka Anny, potom musí byť Jána
babička Anny.
napr:
?-babička(anna,bozena). false. ?-babička(anna,X). false. ?-babička(jana,X). X=anna;
Vyššie sa pýtame na nasledujúce faktami:
- Je Anna babičkou Boženy? Výsledok je
false
. - Čí babička je Anna?
false
(ničí) - Čí babička je Jana? Anny
Ak by tam však táto informácia nebola, program tvrdenie poprie ako v prvom prípade. Aj keby ju tam len niekto zabudol napísať. Podobne ako v SQL tu totiž hovoríme, ako má výsledok vyzerať namiesto toho, ako presne ho vykutáme z dát. V SQL by dotaz na rodokmeň mohol vyzerať napr. Nasledovne:
SELECT vnucka FROM babicka-vnucka WHERE babicka = jana
To bol len príklad.
V logickom programovaní idú veľmi dobre riešiť stavové problémy, napr. Sudoku, hádanka Farmár, vlk, koza, kapusta a podobné ... Týmto sa však s logickým / deklaratívnym programovaním lúčime, logickému programovanie tento kurz nepatrí.
Funkcionálne programovanie
My totiž môžeme na programovanie nazerať ešte z inej strany. Predstavme
si program ako jednu dlhú funkciu, ktorá niečo vezme na vstupe a vráti
výsledok. Majme napr. Funkciu (plus)
, ktorá nám spočíta dve
čísla a funkciu (převrať)
, ktorá prevráti číslo, teda z
21
nám urobí 12
atd ... Zložením funkcií nám
vznikne funkcie magie(x,y) = prevrat.secti(x,y)
, ktorá vezme dve
čísla, spočíta je a výsledok obráti. (Matematicky sa "vláčik" posúva
sprava doľava). Funkcia magie(9,8) = 71
,
magie(4,5) = 9
, magie(10,0) = 9
atď ...
Tento štýl sa v mnohom podobá logickému programovania, ale mnohonásobne ho predčí eleganciou a efektivitou. Chcime napr. Program, ktorý nám vráti v poli všetky deliteľa nejakého čísla:
dejMiDelitele n = [ x | x <- [1 .. n] , n `mod` x == 0 ]
Jednoducho popíšeme, čo chceme robiť. Máme hornú hranicu a chceme
všetky čísla od 1
do tej hranice, pre ktoré platí, že číslo
modulo x
je rovné 0
, čiže x
je
deliteľom čísla n
. Využitie funkcionálního programovania je
teda jasné. Podobný kód s ním dokážeme písať oveľa efektívnejšie a to
bez vedľajších efektov. Čo to znamená?
Imperatívne jazyky pracujú a sekundárne usadzovanie do premenných as manipuláciou s nimi. Či už premennú zmenšíme, zväčšíme, pošleme na zásobník, uložíme do poľa, vytlačíme atď., Pracujeme s pamäťou. To je nebezpečné a niekedy tiež zdĺhavé. Funkcionálne programovaní na to ide z druhej strany. Vyrobíme si "mašinku", do ktorej niečo vsunieme a na konci z nej vypadne výsledok. Vďaka tomu nemáme problém napr. S paralelizmem, proste dáme vedľa seba 3 mašinky, dáme do nich 3 vstupy a dostaneme 3 výstupy. A ak robíme Big dáta, určite ich chceme robiť paralelne, pretože by to inak trvalo naozaj dlho
Ďalej je tu iný spôsob písania programov. U funkcií chceme, aby na jasný vstup dala jasný výstup. To má za následok jednoduché ladenie programov, lebo pokiaľ každá funkcia robí to, čo má, pre daný výsledok je len okamihu pospája za seba. Druhým veľmi príjemným dôsledkom je, že skladáme za seba malé programy. Väčšinou sa zmestia do 10 riadok. Opäť, výsledok sa dostaví hneď, akonáhle vám začne vstup hádzať nečakaný výstup.
Vývojové prostredie
Teraz sa ešte zmienime o prostredí, ktoré budeme používať. Je to GHCi a nájdete ho ako pre Windows i Linux. Výhodou je jeho jednoduchosť. Aj keď nie ste na programovanie v konzole zvyknutí, napriek tomu odporúčam tento nástroj, pretože je to presná ukážka toho, čo by funkcionálne programovanie malo robiť. To, čo má, a ani o trochu naviac Napriek tomu nezúfajte. Okrem kompileru potrebujeme ešte textový editor, na ktorom budeme písať naše funkcie. Na to stačí poznámkový blok, na Linuxe je na to vhodný VIM, ktorý podporuje Haskell syntax, alebo veľa ďalších nástrojov.
Funkcionálne programovacie jazyky
Čo sa týka toho, čo je a čo nie je funkcionálne jazyk, je potrebné si ujasniť, čo od funkcionálního jazyka očakávame. Ak chceme, aby v ňom nešlo písať procedurálne, potom nám mnoho jazykov nezostane. Napríklad Haskell je čisto funkcionálne jazyk. Jazyky ako Dart, Scala, F # sú funkcionálne. Ak však definíciu rozšírime na to, či v jazyku možno písať funkcionálne, potom tam patrí jazyky ako C #, Java, Python atď ... Týmto jazykom sa preto hovorí často multiparadigmatické a v praxi je funkcionálne programovanie naozaj často s záväznými programovaním kombinované. Danú časť programu jednoducho napíšeme prístupom, ktorý je pre ňu efektívnejšie. Funkcionálne programovanie multiparadigmatické jazyky podporujú pomocou tzv. Lambda výrazov. K tomu sa však dostaneme.
Pokračovať budeme nabudúce v lekcii Prvá funkcia v Haskell .