Haskell/Operatory
Z Wikibooks, biblioteki wolnych podręczników.
Spis treści |
[edytuj] Operatory matematyczne i logiczne
| operator | działanie |
|---|---|
| + | dodawanie |
| - | odejmowanie |
| * | mnożenie |
| / | dzielenie |
| ^,^^,** | potęgowanie |
| && | AND |
| || | OR |
| < | mniejszy niż |
| <= | mniejszy lub równy |
| > | większy niż |
| >= | większy lub równy |
| == | równy |
| /= | różny |
[edytuj] Operatory działające na listach
| operator | działanie |
|---|---|
| ++ | konkatenacja list |
| : | dodanie elementu (głowy) do listy, "cons" operator |
| !! | operator indeksowania |
| .. | specyfikacja zasięgu listy |
[edytuj] Przykłady użycia
Operator konkatenacji (++) łączy dwie listy dodając drugą na koniec pierwszej:
Prelude> [1,2,3] ++ [4,5] [1,2,3,4,5]
"Cons" operator tworzy nową listę poprzez dołączenie nowego elementu na początek listy.
Prelude> 1:[2,3,4,5] [1,2,3,4,5]
Operator indeksowania (!!) zwraca element listy o podanym indeksie. Elementy indeksowane są od 0.
Prelude> ['a','b','c','d']!! 2 'c'
Operator (..) służy do tworzenia sekwencji arytmetycznych.
Prelude> [1..5] [1,2,3,4,5] Prelude> [1,3..5] [1,3,5]
Operator (..) może być również zastosowany do tworzenia sekwencji o nieokreślonej długości.
Prelude> [1..]
Wykonanie powyższej instrukcji bezpośrednio w interpreterze nie jest jednak dobrym pomysłem. Interpreter będzie próbował wypisać na ekran całą listę, a ponieważ długość listy nie jest określona, wypisywane będą kolejne liczby, dopóki działanie interpretera nie zostanie przerwane.
[edytuj] Operatory definiowane przez użytkownika
[edytuj] Operatory infiksowe
Operatory infiksowe są funkcjami i podobnie jak "zwykłe" funkcje prefiksowe są definiowane za pomocą równań. Nazwy operatorów składają się z symboli, w przeciwieństwie do identyfikatorów funkcji, które są alfanumeryczne. Poniżej zdefiniowany został operator infiksowy (-^)
(-^) a b = b^a
Prelude> 2 -^ 5 25
Jak widać identyfikator (nazwa) operatora jest zamknięta w nawiasach. Jednak podczas użycia operatora nawiasy już nie występują. Jeśli używając operatora jego nazwa zostanie umieszczona w nawiasach, operator ten zachowuje się jak zwykła funkcja prefiksowa, np.
Prelude> (-^) 2 5 25
Działanie tego operatora jest trochę sztuczne. Przykład ten ma na celu jedynie zaprezentowanie sposobu definicji takich operatorów.
[edytuj] Funkcje jako operatory infiksowe
Operatory infiksowe mogą być używane jako funkcje prefiksowe. Możliwa jest również sytuacja odwrotna. Zdefiniowana wcześniej dwuargumentowa funkcja prefiksowa add może być używana jak funkcja infiksowa.
Prelude> 2 `add` 3 5
Aby funkcja dwuargumentowa mogła być używana jako funkcja infiksowa, jej nazwa musi być ujęta w odwrotne apostrofy.
[edytuj] Sekcje - częściowe użycie operatorów infiksowych
Ponieważ operatory infiksowe są tak naprawdę funkcjami, podobnie jak w przypadku funkcji możliwe jest ich częściowe użycie, nazywane w tym przypadku sekcją. Dzięki zastosowaniu sekcji można zdefiniować na przykład funkcję realizującą inkrementację.
inc = (+1)
w podobny sposób można zdefiniować również dodawanie dwóch liczb:
add = (+)
Sekcje można tworzyć również z funkcji infiksowych, na przykład:
inc = (1 `add`)
Sekcje, podobnie jak funkcje lambda, stanowią bardzo wygodny mechanizm w połączeniu z funkcjami wyższego rzędu. Funkcja map zwiększająca swoje elementy o 1 może wyglądać następująco:
map (+1) [1,2,3,4]
[edytuj] Operatory prefiksowe
Jedynym operatorem prefiksowym występującym w języku Haskell jest operator minus (-), który jest równocześnie operatorem prefiksowym i infiksowym.
[edytuj] Priorytety operatorów
Priorytety operatorów decydują o tym, który operator ma pierwszeństwo wykonania przed innym. Priorytet operatora to liczba całkowita z zakresu od 0 do 9 włącznie (im wyższy tym silniejszy). Priorytet 10 jest zarezerwowany do wywołania funkcji.
Działanie priorytetów najłatwiej zaobserwować na podstawowych operatorach matematycznych. Operator + ma priorytet 6, natomiast operator * 7. Wynika z tego, że mnożenie będzie wykonane wcześniej niż dodawanie.
Prelude> 2 + 2 * 2 6
Jak widać kolejność działań została zachowana zgodnie z ich priorytetami. Jeśli priorytet dodawania byłby taki sam, lub większy niż priorytet mnożenia, wynikiem powyższego działania byłaby liczba 8.
Wynikiem działania 2*3*4 jest oczywiście 24. Nieco zaskakujący może być jednak wynik potęgowania 2^3^4. Można by się spodziewać wyniku 4096 (8^4). Wykonanie takiego działania daje jednak zupełnie inny rezultat:
Prelude> 2^3^4 2417851639229258349412352
Kolejność wykonania operatorów jest określona przez priorytet operatora oraz przez dodatkową właściwość "fixity", która decyduje czy operator wiąże w lewo ("left-associative"), w prawo ("right-associative") czy równorzędnie w obu kierunkach ("non-associative"). Operator * wiąże w lewo, więc najpierw wykonane zostało działanie 2*3, a następnie 6*4. Operator ^ zalicza się do grupy "right-associative" w związku z czym najpierw wykonane zostało działanie 3^4, a następnie 2^81.
Poniższa tabela przedstawia priorytety standardowych operatorów.
| Priorytet | Left associative | Non-associative | Right associative |
|---|---|---|---|
| 9 | !! | . | |
| 8 | ^, ^^, ** | ||
| 7 | *, /, `div`,`mod`, `rem`, `quot` | ||
| 6 | +, - | ||
| 5 | :, ++ | ||
| 4 | ==, /=, <, <=, >, >=,`elem`, `notElem` | ||
| 3 | && | ||
| 2 | || | ||
| 1 | >>, >>= | ||
| 0 | $, $!, `seq` |
[edytuj] Definiowanie zachowania operatorów
Haskell umożliwia tworzenie własnych operatorów oraz określanie sposobu w jaki mają się zachowywać. Do tego celu przeznaczone są trzy funkcje: infix (non-associative), infixl (left-associative) oraz infixr (right-associative). Funkcje te przyjmują jako parametr priorytet oraz identyfikator operatora. Na przykład:
infixr 5 ++ infixl 3 `add`
Standardowy operator ++ jest operatorem wiążącym w prawo i posiada priorytet 5, natomiast operator zdefiniowany przez użytkownika `add` wiąże w lewo i posiada priorytet 3. Jeśli priorytet operatora nie zostanie zdefiniowany ma on domyślną wartość 9.