Haskell/Operatory
Operatory matematyczne i logiczne
edytujoperator | 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
edytujoperator | działanie |
---|---|
++ | konkatenacja list |
: | dodanie elementu (głowy) do listy, "cons" operator |
!! | operator indeksowania |
.. | specyfikacja zakresu listy |
Przykłady użycia
edytujOperator 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
edytujOperatory infiksowe
edytujOperatory 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
edytujOperatory 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
edytujPonieważ 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
edytujJedynym 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
edytujPriorytety 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
edytujHaskell 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
|