Haskell/Operatory

Z Wikibooks, biblioteki wolnych podręczników.

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.