(rekurzivní definice funkcí, funkce vyššího řádu, částečná aplikace, curryifikace, definice funkcí rekurzivně a pomocí kombinátorů, definice vyšších funkcí bez použití formálních parametrů)
To, co dává funkcionálním jazykům vyjadřovací sílu, je možnost definovat nové funkce a neomezovat se na již vestavěné. Funkce ve funkcionálních jazycích definujeme předpisy, ve kterých kromě jména funkce zapisujeme nalevo také její parametry, což budou v nejjednodušším případě jména proměnných.
Třetí mocnina
Maximální hodnota ze tří zadaných
Nové operátory se definují stejným způsobem jako funkce.
Definice operátoru
then u - v else 0
Podstatným důvodem, proč definice nových funkcí zvyšuje naši vyjadřovací sílu, je fakt, že definice mohou být rekurzivní.
Faktoriál
then 1 else n * fact (n - 1)
N tá mocnina
then 1 else a * a^(n - 1)
Rozlišujeme dva druhy rekurze:
Příklad přímé rekurze
map f (x:xs) = f x : map f xs
Příklad nepřímé rekurze
then True else odd (n - 1) odd n = if n == 0 then False else even (n - 1)
Některé programovací jazyky pohlížejí na funkce jen jako na jednoparametrické (unární) funkce. Aplikace funkce na argumentů:
je ve skutečnosti jednoduchou aplikací funkce na jediný argument tedy :
Pomyslný operátor „aplikace“ tedy vlastně sdružuje zleva.
Příklad
Částečná aplikace se říká aplikaci nějaké funkce na „menší počet argumentů než je obvyklé“. Přesněji je to aplikace, jejímž výsledkem je funkce. Například v následující definici je operátor částečně aplikován na argument 3.
add3 = (+) 3
Jeden ze způsobů, jak zachovat víceparametrické funkce, je spojit jejich argumenty do n-tic. Tím se však ztrácí možnost takovéto funkce částečně aplikovat. Dají se však převést na funkce vyšších řádů pomocí procesu zvaného curryifikace.
Uvažme dvě funkce add a add', které realizují sčítání celých čísel, ale mají různý typ.
add :: Int -> Int -> Int add x y = x + y
Funkce add je vyšší funkce, může být aplikována i na jediný argument, tato aplikace vrátí funkci (například add 3 je funkce, která k zadaném argumentu přičítá číslo 3).
add' :: (Int,Int) -> Int add' (x,y) = x + y
Funkce add' požaduje jako argument uspořádanou dvojici a jako výsledek vrací součet prvků dvojice.
Výhoda funkce add oproti funkci add' spočívá právě v tom, že ji můžeme částečně aplikovat na jediný argument, a získat tak zase funkci. Kdybychom chtěli totéž provést s funkcí add' potřebovali bychom k tomu zavedené pomocné funkce, funkce :
add'' x y = add' (x,y)
Obecně z každé funkce , která má jako argument n-tici, je možné vytvořit vyšší funkci, kterou lze postupně aplikovat na n argumentů odpovídajícím složkám n-tice. Tomuto převodu nižších funkcí na vyšší se říká curryifikace.1)
Mějme dvě funkce, f a g, obě se dvěma parametry, definované pomocí stejného výrazu c.
f x y = c
g (x,y) = c
Přechod od funkce g k funkci f je v jazyce Haskell pro binární funkce řešen pomocí funkce curry.
curry :: ((a,b) -> c) -> a -> b -> c curry g x y = g (x,y)
A přechod od funkce f k funkci g je v jazyce Haskell pro binární funkce řešen pomocí funkce uncurry.
uncurry :: (a -> b -> c) -> (a,b) -> c uncurry f (x,y) = f x y
Tedy f = curry g
; g = uncurry f
.
reverse = foldl (flip (:)) []
obsahuje funkci (kombinátor) foldl, která sama ve své definici rekurzi obsahuje, a funkci (kombinátor) flip, která rekurzivní není.
Funkce foldr
foldr _ a [] = a foldr op a (x:s) = x `op` foldr op a s
`op` je infixový zápis aplikace binární funkce.
a `op` b je totez jako op a b
and = foldr (&&) True
or = foldr (||) False concat = foldr (++) [] compose = foldr (.) id
dist f g x = f x (g x)
Vyšší funkce lze často za pomoci kombinátorů definovat tak, že lze vynechat formální parametry – zápis je tak jednodušší, kratší a přehlednější. Například funkci zip lze vyjádřit pomocí funkce zipWith. Funkce zip je zde definována předpisem, který říká, že „zip se chová“ na argumentech s a t stejně jako funkce zipWith (,) na argumentech s a t. 2)
zip s t = zipWith (,) s t
Funkce zip rovna tedy funkci zipWith (,) a původní definici můžeme zkrátit
zip = zipWith (,)
Tomuto zkrácení se říká -redukce (čti eta redukce), vyplývá z tzv. principu extensionality, který říká: Jestliže platí f x = g x pro všechna x ze sjednocení definičních oborů funkcí f a g, pak f = g. (Laicky, když popisujeme funkci pomocí jiné funkce a mají stejné formální parametry ve stejném pořadí, tak je nemusíme uvádět.) 3)
Funkce lze definovat také pomocí vzorů. Vzor popisuje množinu argumentů, které tomuto vzoru vyhovují. Definice funkce se pak rozpadá na více větví, klausulí. Při výpočtu se pak z definice použije ta větev, jejíž vzory vyhovují skutečným argumentům. Vzor je obecněji výraz, jehož žádný podvýraz nelze redukovat – skládá se pouze z proměnných a tzv. konstruktorů.4)
Rekurzivní definici funkce fact lze pomocí vzorů zapsat
fact n = n * fact (n - 1)
Vhodné doplnění studia může být vyzkoušení příkladů ze cvičení prakticky v interpretu haskelu Hugs.
Nebo třeba trošku populárněji psaný seriál o funkcionálním programování na Programujte.com.
O tomto tématu také píše BONUS v jeho příručce Haskellu.
Winsik - ICQ: 215787520 mail: winsik.cz(zavinac)gmail.com
Jitunka - ICQ: 218703195 (jen doplňující informace a poslední dvě podčásti otázky)
Otázku si přečetl pan RNDr. Libor Škarvada a rámcově prošel. Jeho podněty pro doplnění textu, opravy nesrovnalostí a odstranění matoucích či k otázce se nevztahujících textů byly do otázky zaneseny. Tato kontrola je jen rámcová, stále se může stát, že v otázce zůstala zapomenutá chybka či nesrovnalost, vyučující za toto nenese odpovědnost, berte tuto rámcovou kontrolu jako formu pomoci od vyučujících pro studenty.