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.