Przyszło mi się zmierzyć z nowym paradygmatem programowania, mianowicie programowaniem funkcyjnym. Od kilku lat zajmuje się programowaniem. Na początku pisałem imperatywnie, później obiektowo, ale nigdy nie funkcyjnie. Przyznam, że musiałem zmienić odrobinę spojrzenie na kod i samo programowanie. Jednak po krótkiej, ale jakże ciekawej batalii stoczonej na płaszczyźnie mojej klawiatury muszę powiedzieć, że udało się. Jednak spokojna głowa ten paradygmat nie trafi do mojego TOP 3.
Na zajęciach piszemy w języku OCaml i Oz (o pierwszym z nich napiszę później). Teraz skupmy się na języku Oz. Jest to język multi-paradygmatowy, czyli wspomaga kilka paradygmatów. W tym przypadku są jest to:
1. Programowanie w logice
2. Programowanie funkcyjne
3. Programowanie imperatywne
4. Programowanie obiektowe
5. Programowanie deklaratywne
Początki Oz’a sięgają 1991 roku jak to często bywa stworzył go student. Od 1996 roku pieczę nad projektem ma konsorcjum Mozart (często możemy zauważyć w sieci pisownię Oz/Mozart). Projekt jest udostępniany na licencji open source. Składnia języka na początku wydała mi się dość przyjemna, ale z czasem dała ostro popalić. Z języka korzystałem tylko do celów edukacyjnych dlatego nie jestem wstanie powiedzieć wam nic nowego czego nie przeczytacie w dokumentacji i tutorialu (ahhh to co tygryski lubią najbardziej)
Jako, że w internecie jest bardzo mało przykładów związanych z językiem Oz postanowiłem to zmienić. Oto kilka prostych programów.
Funkcja Rev (reverse – przyjmuje jako argument listę) odwracająca listę (rekursja ogonowa).
1
2
3
4
5
6
7
8
9
10
| declare fun {Rev L}
local fun {Pom L Acc}
case L of
nil then Acc
[] H|T then {Pom T H|Acc}
end
end
in {Pom L nil}
end
end |
Funkcja Length która zwraca długość listy przyjętej jako argument.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| declare fun {Length Lst}
local fun {Length_acm Lst Acm}
if Lst==nil then Acm
else {Length_acm Lst.2 Acm+1}
end
end
in {Length_acm Lst 0}
end
end
%przykładowa tablica
declare L = 1|2|3|4|5|nil
%wydrukowanie wyniku
{Show {Length L}} |
Dalej przedstawię zadania z list ćwiczeń, które to rozwiązywałem na zajęciach, niestety nie wszystkie zadania są optymalne obliczeniowo, wiele z nich można usprawnić:
1. Zdefiniuj funkcję Flatten1, która dla argumentu będącego listą list tworzy listę złożoną z elementów wszystkich podlist z zachowaniem ich kolejności, np. {Flatten1 [[5 6] [1 2 3]]} zwraca [5 6 1 2 3], czyli spłaszcza listę o jeden poziom.
1
2
3
4
5
6
7
8
9
10
11
| declare fun {Flatten1 L}
if L==nil then
nil
else
{Append L.1{Flatten1 L.2}}
end
end
declare Lst = [[5 6 1 2 3] nil [3 4 3] [3]]
{Show {Flatten1 Lst}} |
2. Zdefiniuj funkcję Count obliczającą ile razy dany obiekt występuje w danej liście, np. {Count ‘a’ ['a' 'l' 'a']} zwraca 2.
1
2
3
4
5
6
7
8
9
10
| declare fun {Count Z Lst}
if Lst==nil then 0
else
if Z==Lst.1 then 1+{Count Z Lst.2}
else 0+{Count Z Lst.2}
end
end
end
{Show {Count 'a' a|b|a|b|a|b|nil}} |
3. Zdefiniuj funkcję Duplicate powtarzającą dany obiekt określoną liczbę razy i zwracającą wynik w postaci listy, np. {Duplicate ‘la‘ 3} →['la' 'la' 'la'].
1
2
3
4
5
6
7
| declare fun {Duplicate O N}
if N==0 then nil
else O|{Duplicate O N-1}
end
end
{Show {Duplicate 'a' 6}} |
4. Zdefiniuj funkcję Sqr_list podnoszącą do kwadratu wszystkie elementy danej listy liczb, np. {Sqr_list [1 2 ~3]} → [1 4 9].
1
2
3
4
5
6
7
8
9
10
| declare fun {Sqr_list Lst}
if Lst==nil then nil
else
Lst.1*Lst.1|{Sqr_list Lst.2}
end
end
declare L = 3|4|nil
{Show {Sqr_list L}} |
5. Zdefiniuj funkcję Palindrome sprawdzającą, czy dana lista jest palindromem, tj. równa się sobie samej przy odwróconej kolejności elementów, np. {Palindrome ['a' 'l' 'a']} zwraca true.
1
2
3
4
5
6
7
8
9
10
| declare fun {Palindrome Lst}
if Lst==nil then nil
else
Lst=={Reverse Lst}
end
end
declare L = k|a|j|a|k|nil
{Show {Palindrome L}} |
6. Zdefiniuj funkcję ListLength obliczjącą długość dowolnej listy.
1
2
3
4
5
6
7
8
9
10
| declare fun {ListLength Lst}
if Lst==nil then 0
else
1+{ListLength Lst.2}
end
end
declare L = 1|2|3|4|nil
{Show {ListLength L}} |
7. Liczby Fibonacciego są zdefiniowane następująco:
f(0) = 0
f(1) = 1
f(n+2) = f(n) + f(n+1)
Napisz dwie funkcje, które dla danego n znajdują n-tą liczbę Fibonacciego: pierwszą opartą bezpośrednio na powyższej definicji i drugą, wykorzystującą rekursję ogonową. Porównaj ich szybkość wykonania, obliczając np. 37-mą liczbę Fibonacciego.
1
2
3
4
5
6
7
8
9
| declare fun {Fib N}
if N==0 then 0
else
if N==1 then 1
else {Fib N-1}+{Fib N-2}
end
end
end
{Show {Fib 37}} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| declare fun { Fib_tail N}
local fun {Fib N1 N2 I}
if I=<2 then N2
else
local Next = N1+N2 in
{Fib N2 Next I-1}
end
end
end
in {Fib 1 1 N}
end
end
{Show {Fib_tail 37}} |
8. Zdefiniuj funkcję Initsegment : ‘a list * ‘a list -> bool sprawdzającą w czasie liniowym, czy pierwsza lista stanowi początkowy segment drugiej listy. Każda lista jest swoim początkowym segmentem, lista pusta jest początkowym segmentem każdej listy.
1
2
3
4
5
6
7
8
9
10
11
| declare fun {Initsegment L1 L2}
case L1 of
nil then true
[] H|T then
if H==L2.1 then {Initsegment T L2.2}
else false
end
end
end
{Show {Initsegment 2|3|nil 1|2|3|4|nil}} |
OK myślę, że na razie te kila przykładów wystarczy, mam w zanadrzu jeszcze kilka mini-programików, myślę że z czasem je udostępnię. Jak sami widzicie jestem jeszcze dość zielony w paradygmacie funkcyjnym, ale powoli nabieram zrozumienia wobec tej „filozofii”. Mam szczerą nadzieję, że analiza tych kilku programów pozwoli wam szybciej oswoić się z nowym językiem, a przede wszystkim paradygmatem.
Pozdrawiam PL