Programowanie funkcyjne/Procedury wyższych rzędów i listy/Ćwiczenia: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Linia 211: Linia 211:
Za pomocą <tt>map</tt> i <tt>flatten</tt> zapisz procedurę <tt>heads</tt>, której wynikiem dla danej listy jest lista pierwszych elementów list składowych.  
Za pomocą <tt>map</tt> i <tt>flatten</tt> zapisz procedurę <tt>heads</tt>, której wynikiem dla danej listy jest lista pierwszych elementów list składowych.  
}}  
}}  
{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
   '''let''' heads l = flatten (map ('''function''' [] -> [] | h::_ -> [h]) l) ;;
   '''let''' heads l = flatten (map ('''function''' [] -> [] | h::_ -> [h]) l) ;;
</div></div>}}
</div></div>


{{cwiczenie|[Quick-sort]||
{{cwiczenie|[Quick-sort]||
Korzystając z <tt>filter</tt> zaimplementuj alorytm quick-sort.  
Korzystając z <tt>filter</tt> zaimplementuj alorytm quick-sort.  
}}
}}
{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
  '''let rec''' qsort l =  
  '''let rec''' qsort l =  
   '''match''' l '''with'''  
   '''match''' l '''with'''  
Linia 226: Linia 230:
     h::t -> qsort (filter ((>) h) t) @ h :: qsort (filter ((<=) h) t);;
     h::t -> qsort (filter ((>) h) t) @ h :: qsort (filter ((<=) h) t);;
  ''val qsort : 'a list -> 'a list = <fun>''
  ''val qsort : 'a list -> 'a list = <fun>''
</div></div>}}
</div></div>




Linia 242: Linia 246:
  kompresuj [1; 2; 2; 5; 11; 11; 2];;
  kompresuj [1; 2; 2; 5; 11; 11; 2];;
  ''- : int list = [1; 6; 9; 42; 3]''
  ''- : int list = [1; 6; 9; 42; 3]''
{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
   '''let''' kompresuj l =  
   '''let''' kompresuj l =  
     '''if''' l = [] '''then''' []
     '''if''' l = [] '''then''' []
Linia 255: Linia 261:
         )
         )
       );;
       );;
</div></div>}}
</div></div>


{{cwiczenie|[Tails]||
{{cwiczenie|[Tails]||
Linia 264: Linia 270:
uporządkowaną wg. malejących ich długości.  
uporządkowaną wg. malejących ich długości.  
}}
}}
{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
  '''let''' tails l = fold_right ('''fun''' x (h::t) -> (x::h)::(h::t)) l [[]];;
  '''let''' tails l = fold_right ('''fun''' x (h::t) -> (x::h)::(h::t)) l [[]];;
  ''val tails : 'a list -> 'a list list = <fun>''
  ''val tails : 'a list -> 'a list list = <fun>''
</div></div>}}
</div></div>




Linia 277: Linia 285:
Napisz procedurę <tt>uśrednienie</tt>, która dla danej listy obliczy jej uśrednienie.  
Napisz procedurę <tt>uśrednienie</tt>, która dla danej listy obliczy jej uśrednienie.  
}}
}}
{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
  '''let''' usrednienie l =  
  '''let''' usrednienie l =  
   '''match''' l '''with'''  
   '''match''' l '''with'''  
Linia 286: Linia 296:
       '''in''' rev w;;
       '''in''' rev w;;
  ''val usrednienie : float list -> float list = <fun>''
  ''val usrednienie : float list -> float list = <fun>''
</div></div>}}
</div></div>




Linia 314: Linia 324:
}}
}}


{{rozwiazanie|[Rozwiązanie nieefektywne]||
 
  <div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie [Rozwiązanie nieefektywne]</span>
<div class="mw-collapsible-content" style="display:none">
   '''let''' infix t =  
   '''let''' infix t =  
     '''let''' merge x l r = l @ (x :: r)
     '''let''' merge x l r = l @ (x :: r)
     '''in'''  fold_tree merge [] t;;
     '''in'''  fold_tree merge [] t;;
   </div></div>}}
   </div></div>
 


{{rozwiazanie|[Rozwiązanie efektywne]||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
  <div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie [Rozwiązanie efektywne]</span>
<div class="mw-collapsible-content" style="display:none">
   '''let''' infix t =  
   '''let''' infix t =  
     '''let''' merge x l r = '''fun''' acc -> l (x :: (r acc))
     '''let''' merge x l r = '''fun''' acc -> l (x :: (r acc))
     '''and''' id x = x
     '''and''' id x = x
     '''in'''  fold_tree merge id t [];;
     '''in'''  fold_tree merge id t [];;
   </div></div>}}
   </div></div>


{{cwiczenie|[Liczba widocznych elementów]||
{{cwiczenie|[Liczba widocznych elementów]||
Linia 334: Linia 348:
}}
}}


{{rozwiazanie|||
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">
Rozwiązanie polega na tym, że <tt>fold_tree</tt> wyznacza procedurę, która po podaniu największej liczby na ścieżce do korzenia oblicza listę  
Rozwiązanie polega na tym, że <tt>fold_tree</tt> wyznacza procedurę, która po podaniu największej liczby na ścieżce do korzenia oblicza listę  
wartości widocznych w poddrzewie.  
wartości widocznych w poddrzewie.  
Linia 347: Linia 363:
     fold_tree merge ('''fun''' _ -> 0) t (-1);;
     fold_tree merge ('''fun''' _ -> 0) t (-1);;
  ''val widoczne : tree -> int = <fun>''
  ''val widoczne : tree -> int = <fun>''
</div></div>}}
</div></div>





Wersja z 14:41, 1 cze 2020

Praca domowa

W rozwiązaniach poniższych zadań zamiast rekurencji, należy użyć standardowych procedur wyższych rzędów przetwarzających listy.

  • Zapisz procedurę append za pomocą fold_right lub fold_left.
  • Napisz procedurę, która dla danej listy funkcji oblicza ich złożenie.
  • Napisz procedurę obliczającą sumę elementów listy występujących po ostatniej liczbie ujemnej (lub wszystkich, jeżeli na liście nie ma liczb ujemnych).


Ćwiczenia

W rozwiązaniach poniższych zadań zamiast rekurencji, należy użyć standardowych procedur wyższych rzędów przetwarzających listy.

Ćwiczenie [Ciągi różnicowe]

Przypomnij sobie zadanie o ciągu różnicowym danej listy liczb całkowitych. Rozwiąż je za pomocą procedur wyższych rzędów.

Rozwiązanie

Rozwiązanie


Ćwiczenie [Nawiasy]

Dany jest ciąg nawiasów, otwierających i zamykających. Napisz procedurę nawiasy, która obliczy minimalną liczbę nawiasów które należy obrócić, tak aby uzyskać poprawne wyrażenie nawiasowe. Jeżeli nie jest to możliwe, to należy podnieść wyjątek NieDaSie.

 exception NieDaSię;;
 type nawias = Otwierający | Zamykający;;
 let nawiasy  (l: nawias list) = ... ;;

Ćwiczenie [Suma funkcji]

Napisz procedurę, która dla danej listy funkcji, oblicza funkcję będącą sumą funkcji z danej listy.

Rozwiązanie

Ćwiczenie [Łączenie funkcji]

Rozszerz rozwiązanie poprzedniego zadania tak, żeby zamiast dodawania można było zastosować dowolną dwuargumentową operację na wynikach funkcji.

Rozwiązanie

Ćwiczenie [Exists]

Napisz procedurę exists, która dla danego predykatu i listy sprawdzi, czy na liście jest element spełniający predykat. Wykorzystaj wyjątki tak, aby nie przeglądać listy, gdy to już nie jest potrzebne.

Rozwiązanie


Ćwiczenie [Forall]

Napisz procedurę negującą predykat non: ('a -> bool) -> ('a -> bool). Za pomocą tej procedury oraz procedury exists zdefiniuj procedurę forall, która sprawdza, czy dany predykat jest spełniony przez wszystkie elementy danej listy. Czy zastosowanie wyjątków w implementacji procedury exists nadal powoduje, że przeglądane są tylko niezbędne elementy listy?

Ćwiczenie [Sumy]

Napisz procedurę sumy: int list -> int list, której wynikiem dla danej listy [x1,,xn] jest lista: [x1,x1+x2,x1+x2+x3,,x1+x2++xn].

Przykład: sumy [1; 5; 2; 7; 12; 10; 5] = [1; 6; 8; 15; 27; 37; 42].

Ćwiczenie [Podział na listy rosnące]

Napisz procedurę podzial: int list -> int list list, która dla danej listy liczb całkowitych l=[x1;x2;;xn] podzieli ją na listę list [l1;;lk], przy czym:

  • Parser nie mógł rozpoznać (błąd składni): {\displaystyle l = l_1 @ \dots @ l_k} ,
  • każda z list li jest ściśle rosnąca,
  • k jest najmniejsze możliwe.

Przykład: podzial [1;3;0;-2;-2;4;9] = [[1; 3]; [0]; [-2]; [-2;4;9]].

Ćwiczenie [Podział na listy liczb tego samego znaku]

Napisz procedurę podzial: int list -> int list list, która dla danej listy liczb całkowitych l=[x1;x2;;xn] podzieli ją na listę list [l1;;lk], przy czym:

  • Parser nie mógł rozpoznać (błąd składni): {\displaystyle l = l_1 @ \dots @ l_k} ,
  • dla każdej listy li wszystkie elementy na takiej liście są tego samego znaku,
  • k jest najmniejsze możliwe.

Przykład: podzial [1;3;0;-2;-2;-4;9] = [[1; 3]; [0]; [-2;-2;-4]; [9]].

Ćwiczenie [Prefiksy zakończone zerami]

Napisz procedurę prefiksy : int list -> int list list, która dla danej listy liczb całkowitych zwróci listę wszystkich jej prefiksów zakończonych zerem, w kolejności rosnącej ich długości. Przykład: prefiksy [0;1;2;3;0;5;6;0;1] = [[0]; [0;1;2;3;0]; [0;1;2;3;0;5;6;0]]. Zwróć uwagę na złożoność rozwiązania.

Ćwiczenie [Zamiana iloczynu sum na sumę iloczynów]

Wyobraźmy sobie, że jest dana lista list elementów:

[[x1,1;x1,2;;x1,n1];[x2,1;x2,2;;x2,n2];;[xk,1;xk,2;;xk,nk]

Reprezentuje ona iloczyn sum elementów postaci:

(x1,1+x1,2++x1,n1)(x2,1+x2,2++x2,n2)(xk,1+xk,2++xk,nk)

Taki iloczyn sum jest równy sumie iloczynów postaci:

(x1,1x2,1xk,1)+(x1,1x2,1xk,2)++(x1,n1x2,n2xk,nk)

którą z kolei można przedstawić jako listę list postaci:

[[x1,1;x2,1;;xk,1];[x1,1;x2,1;;xk,2];[x1,n1;x2,n2;;xk,nk]]

Napisz procedurę rozwin: 'a list list -> 'a list list, która przekształca listę list reprezentującą iloczyn sum w listę list reprezentującą sumę iloczynów. Kolejność składników w sumie i czynników w iloczynach może być dowolna.

Przykład: rozwin [[1;2;3]; [4]; [5;6]] = [[1; 4; 5]; [1; 4; 6]; [2; 4; 5]; [2; 4; 6]; [3; 4; 5]; [3; 4; 6]].


Rozwiązanie

Laboratorium

W rozwiązaniach poniższych zadań zamiast rekurencji, należy użyć standardowych procedur wyższych rzędów przetwarzających listy.

Ćwiczenie [Heads]

Za pomocą map i flatten zapisz procedurę heads, której wynikiem dla danej listy jest lista pierwszych elementów list składowych.

Rozwiązanie

Ćwiczenie [Quick-sort]

Korzystając z filter zaimplementuj alorytm quick-sort.

Rozwiązanie


Ćwiczenie [Kompresja ciągu liczb]

Rozważmy następującą metodę kompresji ciągów liczb całkowitych: jeżeli w oryginalnym ciągu ta sama liczba powtarza się kilka razy z rzędu, to jej kolejne wystąpienia reprezentujemy za pomocą jednej tylko liczby. Konkretnie, i powtórzeń liczby k reprezentujemy w ciągu skompresowanym jako 2i1(2k1).

Napisz procedury: kompresującą i dekompresującą zadaną listę. Lista skompresowana powinna być oczywiście jak najkrótsza. Przy dekompresji możesz założyć, że lista skompresowana nie zawiera zer.

kompresuj [1; 2; 2; 5; 11; 11; 2];;
- : int list = [1; 6; 9; 42; 3]

Rozwiązanie

Ćwiczenie [Tails]

Załóżmy, że dana jest lista [x1;x2;;xn]. Sufiksem tej listy nazwiemy każdą listę, którą można uzyskać przez usunięcie pewnej liczby (od 0 do n) jej początkowych elementów. Tak więc sufiksami danej listy będzie np. ona sama, pusta lista, a także [x3;x4;;xn]. Napisz procedurę tails: 'a list -> 'a list list, która dla danej listy tworzy listę wszystkich jej sufiksów, uporządkowaną wg. malejących ich długości.

Rozwiązanie


Ćwiczenie [Uśrednienie listy]

Dana jest lista liczb zmiennopozycyjnych [x1;x2;;xn]. Jej uśrednienie, to lista postaci: [x1+.x22.0;;xn1+.xn2.0]. Uśrednieniem listy jednoelementowej oraz pustej jest lista pusta. Napisz procedurę uśrednienie, która dla danej listy obliczy jej uśrednienie.

Rozwiązanie


Ćwiczenie [Od końca do końca]

Napisz funkcję od_końca_do_końca: int list -> int, która dla danej niepustej listy [a1;...;an]$ obliczy mini=1,2,,n|aian+1i|.


Ćwiczenie [Fold_tree]

Dane są: definicja typu tree i procedura fold_tree:
type 'a tree = Node of 'a * 'a tree * 'a tree | Leaf;;
let rec fold_tree f a t = 
  match t with
    Leaf -> a |
    Node (x, l, r) -> f x (fold_tree f a l) (fold_tree f a r);;

Użyj procedury fold_tree do:

  • Policzenia liczby węzłów w drzewie.
  • Policzenia wysokości drzewa.
  • Policzenia średnicy drzewa (tj. najdłuższej prostej ścieżki w drzewie łączącej dwa liście).
  • Sprawdzenia czy drzewo jest drzewem BST (dla drzewa liczb float).

Ćwiczenie [Obejście infiksowe]

Wyznacz listę wartości w węzłach danego drzewa w kolejności infiksowej, używając procedury fold_tree. Czy potrafisz to zrobić efektywnie?


Rozwiązanie [Rozwiązanie nieefektywne]


Rozwiązanie [Rozwiązanie efektywne]

Ćwiczenie [Liczba widocznych elementów]

Powiemy, że liczba w węźle drzewa jest widoczna, jeżeli na ścieżce od tego węzła do korzenia drzewa nie ma większej liczby. W szczególności liczba w korzeniu drzewa jest zawsze widoczna, a liczby mniejsze od niej nie są nigdy widoczne. Napisz procedurę widoczne:drzewo int, która dla zadanego drzewa (zawierającego wyłącznie liczby nieujemne) obliczy liczbę widocznych liczb. Rozwiązując to zadanie należy skorzystać z procedury fold_tree. Możesz założyć, że w drzewie nie ma liczb ujemnych.


Rozwiązanie


Ćwiczenie [Procedury wyższych rzędów dla drzew]

Przypomnij sobie rozwiązanie jednego z poprzednich zadań, gdzie trzeba było zdefiniować typ danych reprezentujący drzewa dowolnego (skończonego) stopnia. Zdefiniuj dla takich drzew odpowiedniki procedur map, filter i fold.

W procedurze filter, jeżeli odrzucamy jakiś wierzchołek, to odrzucamy również wszystkich jego potomków. Odpowiednik procedury fold powinien być sparametryzowany dwiema funkcjami: Jedna powinna działać "w poziomie", kumulując wyniki policzone dla poddrzew zakorzenionych w synach danego węzła. Druga powinna działać "w pionie", okreslając wynik dla poddrzewa zakorzenionego w danym węźle, na podstawie wartości przechowywanej w tym węźle oraz wyniku skumulowanego dla poddrzew zakorzenionych w jego synach.