Haskell/Operatory
Operatory matematyczne i logiczne
[edytuj]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 |
Operatory działające na listach
[edytuj]operator | działanie |
---|---|
++ | konkatenacja list |
: | dodanie elementu (głowy) do listy, "cons" operator |
!! | operator indeksowania |
.. | specyfikacja zakresu listy |
Przykłady użycia
[edytuj]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.
Operatory definiowane przez użytkownika
[edytuj]Operatory infiksowe
[edytuj]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.
Funkcje jako operatory infiksowe
[edytuj]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.
Sekcje - częściowe użycie operatorów infiksowych
[edytuj]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]
Operatory prefiksowe
[edytuj]Jedynym operatorem prefiksowym występującym w języku Haskell jest operator minus (-), który jest równocześnie operatorem prefiksowym i infiksowym.
Priorytety operatorów
[edytuj]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` |
Definiowanie zachowania operatorów
[edytuj]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.
Uwaga!
Prelude> 3 `add` 4 * 4 19 Zastosowanie add jako funkcji da jednak inny wynik: Prelude> add 3 4 * 4 28 Użycie funkcji zawsze ma najwyższy priorytet. |
Ćwiczenie
|