Haskell/Więcej na temat funkcji
Funkcje
[edytuj]"Curried functions"
[edytuj]"Currying" to sposób transformacji funkcji przyjmującej wiele argumentów do funkcji przyjmującej jeden argument. Technika ta została nazwana "currying" przez Christophera Strachey na cześć matematyka Haskella Curry, mimo iż została stworzona przez Mosesa Schönfinkela i Gottloba Frege'a. |
Funkcje "curried"[1] to funkcje przyjmujące N parametrów, które możemy traktować jako funkcje przyjmujące jeden parametr i zwracające funkcje przyjmującą N-1 parametrów.[2]
Poniższa definicja przedstawia funkcję, która dodaje dwa przekazane jej argumenty:
add :: Integer -> Integer -> Integer add a b = a + b
Jest to przykład funkcji "curried". Definicję typu powyższej funkcji można zapisać jako:
add :: Integer -> (Integer -> Integer) add a b = a + b
jest to funkcja przyjmująca parametr typu Integer oraz zwracająca funkcję typu Integer->Integer (przyjmującą i zwracającą liczbę całkowitą)
Użycie tej funkcji:
add a b
jest więc równoznaczne z:
(add a) b
Zastosowanie add do pierwszego argumentu zwraca nową funkcję, która jest stosowana do drugiego argumentu.
Currying jest możliwy dzięki temu, że
- użycie funkcji wiąże w lewo, więc f x y jest równoznaczne z (f x) y
- operator -> wiąże w prawo, więc a->a->a jest równoznaczne z a->(a->a)
Wykorzystując własności funkcji "curried" można szybko i wygodnie definiować nowe funkcje poprzez tzw. częściowe użycie już istniejących funkcji. Na przykład, aby zdefiniować funkcję realizującą inkrementację wystarczy napisać:
inc = add 1
Powyższy przykład pokazuje jedną z sytuacji, w których funkcja może być traktowana jako wartość zwracana przez inną funkcję. Kolejny przykład przedstawia sytuację, w której funkcja jest przekazywana jako argument innej funkcji.
map :: (a->b)->[a]->[b] map f [] = [] map f (x:xs)= f x : map f xs
Przykład ten pokazuje jeszcze jedną ważną własność funkcji. Użycie funkcji ma wyższy priorytet niż jakikolwiek operator infiksowy. Dzięki temu
f x : map f xs
jest równoznaczne z
(f x) : (map f xs)
Funkcje wyższego rzędu (higher-order functions)
[edytuj]Przykładem zastosowania funkcji map może być:
map (add 1) [1,2,3] [2,3,4]
Funkcja map wykorzystuje inną funkcję (funkcję zwracaną przez add 1). Tego typu funkcje nazywane są funkcjami wyższego rzędu.
Funkcje wyższego rzędu są stosowane w językach funkcyjnych bardzo często. Bez ich istnienia w zasadzie ciężko sobie wyobrazić sens programowania funkcyjnego.
Kolejny przykład to funkcja wyznaczająca liczbę elementów listy spełniających warunek "p".
numOf p xs = length (filter p xs)
Zarówno funkcja filter, jak i numOf są funkcjami wyższego rzędu.
Funkcje Lambda
[edytuj]Rachunek lambda - system formalny używany do badania zagadnień związanych z podstawami matematyki jak rekurencja, definiowalność funkcji, obliczalność, podstawy matematyki np. definicja liczb naturalnych itd. Rachunek lambda został wprowadzony przez Alonzo Churcha i Stephen Cole Kleene w 1930 roku.
Rachunek lambda jest przydatny do badania algorytmów. Wszystkie algorytmy, które dadzą się zapisać w rachunku lambda, dadzą się zaimplementować na maszynie Turinga i odwrotnie. |
Wszystkie definiowane dotychczas funkcje posiadały swoją nazwę oraz równania opisujące działanie tej funkcji. Haskell umożliwia również definiowanie funkcji nienazwanych (anonimowych). Realizowane jest to za pomocą tzw. "lambda abstraction". Na przykład funkcja realizująca inkrementację może być zapisana następująco:
\x -> x+1
Funkcje lambda równie dobrze mogą przyjmować więcej niż 1 argument.
\x -> \y -> x+y
Powyższa funkcja wykonuje dodawanie dwóch liczb. Można ją również zdefiniować w krótszy sposób:
\x y -> x+y
Funkcje lambda mogą być również użyte do definiowania funkcji nazwanych. Na przykład
add = \x y -> x+y
jest równoznaczne z
add x y = x+y
Funkcje lambda są bardzo wygodne podczas wykorzystania funkcji wyższego rzędu. W jednym z powyższych przykładów została wykorzystana funkcja map, która za pomocą funkcji inc zwiększała o 1 każdy z elementów listy. Do jej wykorzystania konieczna była osobna definicja funkcji inc. Funkcje lambda pozwalają na zdefiniowanie funkcji zwiększającej element o 1 bezpośrednio podczas użycia funkcji map.
map (\x -> x+1) [1,2,3]
Obsługa błędów - funkcja error
[edytuj]Podstawowa obsługa błędów w Haskellu jest realizowana za pomocą funkcji error. Funkcja ta przyjmuje komunikat w postaci łańcucha znaków i (w rozsądnych implementacjach Haskella) wypisuje ten komunikat na ekran jako błąd programu. Na przykład definicja funkcji head z the Standard Prelude wygląda następująco:
head (x:xs) = x head [] = error "head{PreludeList}: head []"
Funkcję error można oczywiście stosować we własnych funkcjach, na przykład:
podziel a 0 = error "próbujesz dzielić przez zero!" podziel a b = a/b