REKURENCJA W JĘZYKU HASKELL

Transkrypt

REKURENCJA W JĘZYKU HASKELL
REKURENCJA W JĘZYKU
HASKELL
Autor: Walczak Michał
CZYM JEST REKURENCJA?

Rekurencja zwana rekursją, polega na wywołaniu przez funkcję samej siebie.
Algorytmy rekurencyjne zastępują w pewnym sensie iteracje. Zazwyczaj
zadania rozwiązywane tą techniką są wolniejsze od iteracyjnego
odpowiednika, natomiast rozwiązanie niektórych problemów jest znacznie
wygodniejsze. Rekurencja jest często stosowana w matematyce.

W języku Haskell nie istnieją takie instrukcje jak pętle, jest to związane z
tym że nie jest możliwa zmiana wartości zmiennej. Implementacja
powtarzających się czynności musiała zostać rozwiązana przez rekurencję.
Przykład zastosowania rekurencji Ciąg
Fibonacci'ego

Jest to ciąg, gdzie każdy kolejny wyraz ciągu jest sumą dwóch poprzednich.
Dwa pierwsze elementy ciągu musimy wyznaczyć bez rekurencji.
F0=0
F1=1
Fn=Fn-1+Fn-2, dla n≥ 2
Początkowe wartości to: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
Funkcja Maximum
Implementacja w języku
imperatywnym

Najprawdopodobniej
stworzylibyśmy zmienną, która
przechowywałaby największą
wartości oraz pętlę, która przy
wykonywaniu sprawdzałaby kolejne
elementy listy. Jeśli sprawdzany
aktualnie element listy jest
większy od aktualnej maksymalnej
wartości, wtedy zapisalibyśmy ten
element do naszej zmiennej. Kiedy
pętla przestanie się wykonywać
ostatni wynik jest naszym
rozwiązaniem!
Implementacja z użyciem
rekurencji

Funkcja dzieli listę na head i tail, aż
do momentu otrzymania listy
jednoelementowej i porównuje
head, z maximum tail (czyli
wywołuje funkcje maximum dla
tail).
Implementacja w języku Haskell

Pierwszy warunek końcowy zwraca błąd jeśli nasza lista jest pusta.

W wypadku jeśli jest to lista jednoelementowa - wynikiem będzie ten jeden
element.

W trzecim warunku końcowym rozłączamy naszą listę na głowę i ogon.
Używamy funkcji where w celu zdefiniowania funkcji maxTail, jako maximum
reszty listy, a następnie sprawdzamy warunek (if) czy głowa jest większa od
reszty listy. Jeśli tak- zwracamy głowę jako wynik. W innym wypadku
zwracamy maximum reszty listy.
Przykład z użyciem funkcji maximum

Weźmy tablicę liczb całkowitych [2,1,5].

Krok 1:
Dzielimy tę tablicę na head(2) i tail(1,5) i wywołujemy funkcję maximum dla tail
Przykład z użyciem funkcji maximum

Weźmy tablicę liczb całkowitych [2,1,5].

Krok 1:
Dzielimy tę tablicę na head(2) i tail(1,5) i wywołujemy funkcję maximum dla tail

Krok 2:
Tail jest ponownie dzielony na head(1) i tail(5) w wyniku tego podziału mamy
spełniony warunek końca rekurencji.
Przykład z użyciem funkcji maximum

Weźmy tablicę liczb całkowitych [2,1,5].

Krok 1:
Dzielimy tę tablicę na head(2) i tail(1,5) i wywołujemy funkcję maximum dla tail

Krok 2:
Tail jest ponownie dzielony na head(1) i tail(5) w wyniku tego podziału mamy
spełniony warunek końca rekurencji.

Krok 3:
Porównujemy 1 i 5, funkcja zwraca nam 5.
Przykład z użyciem funkcji maximum

Weźmy tablicę liczb całkowitych [2,1,5].

Krok 1:
Dzielimy tę tablicę na head(2) i tail(1,5) i wywołujemy funkcję maximum dla tail

Krok 2:
Tail jest ponownie dzielony na head(1) i tail(5) w wyniku tego podziału mamy
spełniony warunek końca rekurencji.

Krok 3:
Porównujemy 1 i 5, funkcja zwraca nam 5.

Krok 4:
Porównujemy wartość zwróconą w kroku 3 czyli 5 z head czyli 2. 5 jest większe
od 2 zatem funkcja zwraca 5 jako największą wartość tablicy.
Czystszy zapis z użyciem funkcji max
Funkcja replicate

Funkcja replikacja pobiera dwa argument z czego pierwszy to int który mówi
o tym ile razy ma zostać powtórzony drugi argument funkcji.
Wynikiem działania tej funkcji jest lista powtarzających się elementów.
Przykładowe wywołanie funkcji:
replicate 3 5 zwraca nam listę [5,5,5]
Funkcja Take

Funkcja take jest to funkcja, która pobiera zadaną liczbę elementów z
podanej tablicy.
Przykładowe użycie funkcji take 3 [2, 1, 25, 3, 0, 5]
Wynik [2,1,25]
W tym przypadku wykorzystujemy n oraz od razu rozbijamy listę na znany nam
już ogon i głowę. Bierzemy pierwszy element listy i łączymy go poprzez
rekurencję (n-1) razy z pozostałymi elementami.
Funkcja reverse

Jest to funkcja która zwraca odwróconą listę do listy podanej a argumencie
funkcji
Przykładowe użycie funkcji reverse [2,1,4]
Wynik to [4,1,2]
Funkcja QuickSort

Załóżmy, że mamy listę elementów typu Ord do posortowania. Najefektywniej
można to osiągnąć za pomocą popularnego algorytmu QuickSort. W językach
imperatywnych taki algorytm zajmować może nawet kilkanaście linijek kodu,
a jego implementacja w Haskellu jest dużo krótsza i bardziej przejrzysta.

Posortowaną listą nazywamy taką listę, której wszystkie elementy są mniejsze
bądź równe największemu elementowi. Następnie zaczynając od pierwszej
liczby sprawdzamy czy każda następna jest większa od poprzedniej.
Implementacja algorytmu QuickSort

Warunek końca określa sytuację, kiedy do posortowania zostaje pusta lista (co
jest równoznaczne z tym, że jest już posortowana).

Elementy listy, które są mniejsze lub równe headowi są sortowane i
umieszczane na początku listy, następnie wstawiany jest head, a po nim
posortowane elementy większe lub równe headowi.
Podsumowanie

Wykonaliśmy już trochę działań za pomocą rekurencji i powinniśmy zauważać
już pewną zależność. Zazwyczaj definiujemy warunek końcowy, po czym
definiujemy funkcję, która wykonuje operacje pomiędzy niektórymi
elementami względem reszty. Nie ma znaczenia czy to jest drzewo, lista, czy
inna struktura danych. Zazwyczaj warunkiem końcowym jest punkt, gdzie
rekurencja traci sens (otrzymujemy ten sam wynik cały czas). W przypadku
list, najczęściej warunkiem końcowym jest pusta lista. W przypadku drzew jest to punkt bez żadnych dzieci.

Żeby myśleć rekurencyjne za warunek końcowy musimy uznać moment, w
którym rekurencja nie zostaje użyta. Musimy także pamiętać o danych, z
których będziemy korzystać, rozłożeniu parametrów funkcji oraz momencie,
w którym użyjemy rekurencji.

Podobne dokumenty