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

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Kubica (dyskusja | edycje)
Kubica (dyskusja | edycje)
Linia 102: Linia 102:


  '''type''' tree = Node '''of''' tree * int * tree | Leaf;;
  '''type''' tree = Node '''of''' tree * int * tree | Leaf;;
''type tree = Node of tree * int * tree | Leaf''
  '''let''' '''rec''' fold_tree f a t =  
  '''let''' '''rec''' fold_tree f a t =  
   '''match''' t '''with'''
   '''match''' t '''with'''
  Leaf -> a |
    Leaf -> a |
  Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;
    Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;
''val fold_tree : (int -> 'a -> 'a -> 'a) -> 'a -> tree -> 'a = <fun>''


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.  
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.  
Linia 112: Linia 115:
(zawierającego wyłącznie liczby nieujemne) obliczy liczbę widocznych liczb.  
(zawierającego wyłącznie liczby nieujemne) obliczy liczbę widocznych liczb.  
Rozwiązując to zadanie należy  skorzystać z procedury <tt>fold_tree</tt>.
Rozwiązując to zadanie należy  skorzystać z procedury <tt>fold_tree</tt>.
Możesz założyć, że w drzewie nie ma liczb ujemnych.
{{rozwiazanie|prostsze, ale mniej efektywne||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> <div class="mw-collapsible-content" style="display:none">
Rozwiązanie polega na tym, że <tt>fold_tree</tt> nie wyznacza listy widocznych wartości,
ale procedurę, która po podaniu największej liczby na ścieżce do korzenia oblicza listę
wartości widocznych w poddrzewie.
Po wyznaczeniu takiej procedury dla całego drzewa wystarczy jej podać jako maksimum na ścieżce do korzenia
wartość mniejszą od wszystkich wartości w drzewie, np. -1.
'''let''' widoczne t =
  '''let''' a _ = []
  '''and''' f x pl pr m =
    '''if''' x > m '''then'''
      [x] @ pl x @ pr x
    '''else'''
      pl m @ pr m
  '''in'''
    fold_tree f a t (-1);;
''val widoczne : tree -> int list = <fun>''
Nieefektywność tego rozwiązania polega na wielokrotnym sklejaniu list widocznych wartości za pomocą <tt>@</tt>.
Jak to poprawić?
</div></div>}}





Wersja z 14:11, 8 lis 2006

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.

  • Przypomnij sobie zadanie o ciągu różnicowym danej listy liczb całkowitych. Rozwiąż je za pomocą procedur wyższych rzędów.
  • 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) = ... ;;
  • Napisz procedurę, która dla danej listy funkcji, oblicza funkcję będącą sumą funkcji z danej listy.
  • Rozszerz rozwiązanie poprzedniego zadania tak, żeby zamiast dodawania można było zastosować dowolną dwuargumentową operację na wynikach funkcji.
  • 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.
  • 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?

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 zapisz procedurę heads, której wynikiem dla danej listy jest lista pierwszych elementów list składowych.

Ćwiczenie [Quick-sort]

Korzystając z filter zaimplementuj alorytm quick-sort.

Rozwiązanie

{{{3}}}


Ć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]


Ć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

{{{3}}}


Ć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

{{{3}}}


Ć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 tree = Node of tree * int * tree | Leaf;;
type tree = Node of tree * int * tree | Leaf

let rec fold_tree f a t = 
  match t with
    Leaf -> a |
    Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;
val fold_tree : (int -> 'a -> 'a -> 'a) -> 'a -> tree -> 'a = <fun>

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 prostsze, ale mniej efektywne

{{{3}}}


Ć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.