Programowanie funkcyjne/Struktury danych/Ćwiczenia: Różnice pomiędzy wersjami
(Nie pokazano 19 wersji utworzonych przez 2 użytkowników) | |||
Linia 11: | Linia 11: | ||
}} | }} | ||
<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 nieefektywne, ale proste</span> | ||
<div class="mw-collapsible-content" style="display:none"> | |||
'''let rec''' rev l = | '''let rec''' rev l = | ||
'''match''' l '''with''' | '''match''' l '''with''' | ||
Linia 18: | Linia 19: | ||
h::t -> (rev t) @ [h];; | h::t -> (rev t) @ [h];; | ||
Ze względu na koszt operacji <tt>@</tt> to rozwiązanie ma złożoność czasową kwadratową. | Ze względu na koszt operacji <tt>@</tt> to rozwiązanie ma złożoność czasową kwadratową. | ||
</div></div> | </div></div> | ||
<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 efektywne</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
'''let''' rev l = | '''let''' rev l = | ||
'''let rec''' pom l w = | '''let rec''' pom l w = | ||
Linia 28: | Linia 31: | ||
'''in''' | '''in''' | ||
pom l [];; | pom l [];; | ||
</div></div>}} | </div></div> | ||
{{cwiczenie|[Przeplot list]|| | |||
Napisz procedurę <tt>shuffle: α list → α list → α list</tt>, która dla danych dwóch list postaci <math>[x_1; x_2; \dots; x_n]</math> oraz <math>[y_1; y_2; \dots; y_m]</math> wyznaczy listę postaci <math>[x_1; y_1; x_2; y_2; \dots]</math>. | |||
Jeżeli jedna z list jest dłuższa, to jej końcowe elementy trafiają na koniec listy wyikowej. | |||
Na przykład: <tt><nowiki>shuffle [3; 2; 8; 1; 9; 3; 6] [5; 7; 0] = [3; 5; 2; 7; 8; 0; 1; 9; 3; 6].</nowiki></tt> | |||
}} | |||
Linia 36: | Linia 45: | ||
}} | }} | ||
<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''' flatten l = | '''let rec''' flatten l = | ||
'''match''' l '''with''' | '''match''' l '''with''' | ||
[] -> [] | | [] -> [] | | ||
h::t -> h @ flatten t;; | h::t -> h @ flatten t;; | ||
</div></div>}} | </div></div> | ||
{{cwiczenie|[Średnica drzew]||}} | |||
Dana jest definicja typu: <tt>type tree = Node of tree * tree | Leaf</tt>. | |||
Odległością między dwoma wierzchołkami (<tt>Node</tt>) w drzewie nazywamy minimalną liczbę krawędzi jakie trzeba przejść z jednego wierzchołka do drugiego. | |||
Średnicą drzewa nazwiemy maksymalną odległość między dwoma węzłami (<tt>Node</tt>) w drzewie. | |||
Przyjmujemy, że średnica pustego drzewa (<tt>Leaf</tt>) jest równa 0. | |||
Napisz procedurę <tt>srednica: tree -> int</tt>, która oblicza średnicę danego drzewa. | |||
<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"> | |||
Opieramy się na następującej obserwacji: | |||
* Puste drzewo ma średnicę 0. | |||
* Jeśli drzewo jest niepuste, to przez <math>t_1</math> i <math>t_2</math> oznaczmy dwa poddrzewa zakorzenione w lewym i prawym synie korzenia. Odpowiednio przez <math>d_1</math> i <math>d_2</math> oznaczmy średnice tych poddrzew, a przez <math>h_1</math> i <math>h_2</math> ich wysokości. Wówczas średnica całego drzewa wynosi <math>\max(d_1, d_2, h_1+h_2+2)</math>. | |||
Dla każdego wierzchołka w drzewie obliczamy wysokość i średnicę drzewa zakorzenionego w tym wierzchołku. | |||
Przyjmujemy przy tym, że drzewo puste ma średnicę 0 i wysokość -1. | |||
let rec wys_sr t = | |||
match t with | |||
Leaf -> (-1,0) | | |||
Node (t1, t2) -> | |||
let (h1, d1) = wys_sr t1 | |||
and (h2, d2) = wys_sr t2 | |||
in (max h1 h2 + 1, max (max d1 d2) (h1+h2+2));; | |||
let srednica t = snd (wys_sr t);; | |||
</div></div> | |||
{{cwiczenie|[Drzewa dowolnego stopnia]|| | {{cwiczenie|[Drzewa dowolnego stopnia]|| | ||
Linia 51: | Linia 89: | ||
== Laboratorium == | == Laboratorium == | ||
Ćwiczenia programistyczne na listy: | |||
{{cwiczenie|[<math>n</math>-ty element listy]|| | |||
Zaimplementuj procedurę zwracającą <math>n</math>-ty element listy. | |||
}} | |||
<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''' nth l n = | |||
'''match''' l '''with''' | |||
h::t -> | |||
'''if''' n = 0 '''then''' h '''else''' nth t (n-1);; | |||
</div></div> | |||
{{cwiczenie|[Lista <math>[0;1;\dots;n]</math>]|| | |||
Napisz procedurę tworzącą listę <math>n</math> pierwszych liczb naturalnych. | |||
}} | |||
<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''' naturalne n = | |||
'''let rec''' pom k = | |||
'''if''' k >= n '''then''' [] '''else''' k :: pom (k+1) | |||
'''in''' pom 0;; | |||
</div></div> | |||
{{cwiczenie|[append]|| | |||
Zaimplementuj procedurę <tt>append</tt> sklejającą listy. | |||
}} | |||
<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''' append l1 l2 = | |||
'''match''' l1 '''with''' | |||
[] -> l2 | | |||
h::t -> h :: append t l2;; | |||
</div></div> | |||
{{cwiczenie|[ciąg różnicowy]|| | |||
Ciąg różnicowy ciągu <math>(x_1, \dots, x_n)</math> to ciąg postaci <math>(x_2-x_1,\dots,x_n-x_{n-1})</math>. Napisz procedurę obliczającą ciąg różnicowy żądanej listy liczb całkowitych. | |||
}} | |||
<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''' roznicowy l = | |||
'''match''' l '''with''' | |||
h1::(h2::_ '''as''' t) -> (h2-h1) :: roznicowy t | | |||
_ -> [];; | |||
</div></div> | |||
<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 2</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
'''let''' roznicowy l = | |||
'''let rec''' pom a l = | |||
'''match''' l '''with''' | |||
h1::(h2::_ as t) -> pom ((h2-h1)::a) t | | |||
_ -> rev a | |||
'''in''' pom [] l;; | |||
</div></div> | |||
{{cwiczenie|[ciąg ciągów różnicowych]|| | |||
Napisz procedurę obliczającą listę kolejnych ciągów różnicowych zadanej listy liczb całkowitych, tzn.: daną listę, jej ciąg różnicowy, ciąg różnicowy ciągu różnicowego itd. | |||
}} | |||
<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''' roznicowe l = | |||
'''if''' l = [] '''then''' [[]] '''else''' l :: roznicowe (roznicowy l);; | |||
</div></div> | |||
{{cwiczenie|[minimum-maksimum]|| | |||
Napisz procedurę, której wynikiem jest para: minimum i maksimum elementów z listy. Procedura ta powinna wykonywać co najwyżej <math>\frac{3}{2} n</math> porównań. | |||
}} | |||
{{cwiczenie|[<tt>last</tt>]|| | |||
Napisz procedurę <tt>last</tt>, której wynikiem jest ostatni element (niepustej) listy. | |||
}} | |||
<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''' last (h::t) = | |||
'''if''' t = [] '''then''' h '''else''' last t;; | |||
</div></div> | |||
{{cwiczenie|[<tt>head</tt> i <tt>tail</tt>]|| | |||
Napisz procedury <tt>head</tt> i <tt>tail</tt>, które dla zadanej listy <math>l</math> i liczby całkowitej <math>n</math> zwracają pierwsze/ostatnie <math>n</math> elementów listy <math>l</math>. Jeśli lista <math>l</math> ma mniej elementów niż <math>n</math>, to wynikiem powinna być cała lista <math>l</math>. (Nazwy pochodzą od programów <tt>head</tt> i <tt>tail</tt> w Uniksie.) Co jest wynikiem <tt>tail (head l n) 1</tt>?}} | |||
<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''' head l n = | |||
'''let rec''' head_pom l n a = | |||
'''if''' (n = 0) || (l = []) '''then''' | |||
rev a | |||
'''else''' | |||
head_pom (tl l) (n-1) ((hd l)::a) | |||
'''in''' head_pom l n [];; | |||
'''let''' tail l n = | |||
'''let rec''' skip l k = | |||
'''if''' k = 0 '''then''' l '''else''' skip (tl l) (k-1) | |||
'''in''' | |||
skip l (max (length l - n) 0);; | |||
</div></div> | |||
{{cwiczenie|[sortowanie]|| | |||
Zaimplementuj sortowanie: przez scalanie lub quick-sort. | |||
}} | |||
{{cwiczenie|[arytmetyka nieograniczonych liczb całkowitych]|| | |||
Zaimplementuj pakiet arytmetyczny nieograniczonych liczb całkowitych. | |||
}} | |||
{{cwiczenie|[lista większościowa]|| | |||
Lista większościowa to taka lista, na której ponad połowa elementów jest sobie równa, a ich wartość nazywa się właśnie większością. Napisz procedurę wybierającą większość z listy większościowej; uzasadnij jej poprawność.}} | |||
<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''' majority l = | |||
'''let rec''' scan c k l = | |||
'''match''' l '''with''' | |||
[] -> c | | |||
h::t -> | |||
'''if''' k = 0 '''then''' scan h 1 t | |||
'''else if''' c = h '''then''' scan c (k+1) t | |||
'''else''' scan c (k-1) t | |||
'''in''' scan (hd l) 0 l;; | |||
</div></div> | |||
{{cwiczenie|[trójki]|| | |||
Napisz procedurę <tt>trójki:int list -> (int * int * int) list</tt>, która dla zadanej listy dodatnich liczb całkowitych, uporządkowanej rosnąco, stworzy listę takich trójek <math>(a, b, c)</math> liczb z danej listy, że: | |||
* <math>a < b < c</math>, | |||
* liczby <math>a</math>, <math>b</math> i <math>c</math> spełniają nierówność trójkąta, czyli <math>c < a + b</math>. | |||
}} | |||
Ćwiczenia na inne struktury danych: | Ćwiczenia na inne struktury danych: | ||
{{cwiczenie|[arytmetyka liczb wymiernych]|| | |||
Zaimplementuj arytmetykę liczb wymiernych, reprezentując liczby wymierne jako rekordy złożone z licznika i mianownika. Implementacja może być uproszczona, np. bez skracania ułamków i bez normalizacji znaków. | |||
}} | |||
{{cwiczenie|[reprezentacja daty]|| | |||
Zdefiniuj typ reprezentujący dni tygodnia, miesiące i datę. | |||
}} | |||
{{cwiczenie|[dzień tygodnia]|| | |||
Zdefiniuj procedurę obliczającą na podstawie daty dzień tygodnia. Możesz założyć, że dana jest procedura <tt>sylwester</tt>, która na podstawie roku określa jakiego dnia tygodnia był Sylwester. | |||
}} | |||
{{cwiczenie|[słaby warunek BST]|| | |||
Napisz procedurę, która dla dowolnego drzewa binarnego poprawia je tak, że spełnia ono słabszą wersję warunku BST: dla dowolnego węzła, lewy syn nie jest większy, a prawy nie jest mniejszy niż węzeł. | |||
}} | |||
{{cwiczenie|[wyważanie drzewa BST]|| | |||
Napisz procedurę, która przekształca dane drzewo binarne w wyważone drzewo binarne, zachowując kolejność elementów w porządku infiksowym. | |||
}} | |||
{{cwiczenie|[zbiory jako drzewa BST]|| | |||
Zaimplementuj takie operacje na zbiorach (reprezentowanych jako drzewa BST) jak suma, przecięcie i różnica. | |||
}} | |||
{{cwiczenie|[kolejki dwustronne]|| | |||
Rozszerz implementację kolejek o wkładanie i wyjmowanie elementów z obydwu stron. Jaką własność powinna zachowywać procedura <tt>balance</tt>, aby koszt zamortyzowany operacji był stały? | |||
}} | |||
{{cwiczenie|[liczby naturalne]|| | |||
Dany jest typ danych reprezentujący (mało efektywnie) liczby naturalne:}} | |||
'''type''' nat = ZERO | SUCC of nat;; | |||
Zaimplementuj operacje arytmetyczne na takich liczbach. |
Aktualna wersja na dzień 13:57, 1 cze 2020
Praca domowa
- Napisz procedurę dubluj, która dubluje elementy listy, na przykład dubluj [1;2;3] = [1;1;2;2;3;3].
- Punkty (kratowe) na płaszczyźnie reprezentujemy jako pary liczb całkowitych. Prostokąty o bokach równoległych do osi układu współrzędnych reprezentujemy jako pary punktów: dolny lewy i górny prawy róg. Napisz procedurę, która dla dwóch danych prostokątów wyznacza ich przecięcie. Podaj w jaki sposób reprezentowany jest zbiór pusty.
- Zadeklaruj wariantowy typ danych reprezentujący abstrakcyjną składnię wyrażeń arytmetycznych. Napisz procedurę obliczającą wartość wyrażenia.
Ćwiczenia
Ćwiczenie [rev]
Zaimplementuj procedurę rev odwracającą listę. (Zwróć uwagę, że rozwiązując to zadanie można korzystać z :: lub @, co daje liniową lub kwadratową złożoność czasową.)
Rozwiązanie nieefektywne, ale proste
Rozwiązanie efektywne
Ćwiczenie [Przeplot list]
Napisz procedurę shuffle: α list → α list → α list, która dla danych dwóch list postaci oraz wyznaczy listę postaci . Jeżeli jedna z list jest dłuższa, to jej końcowe elementy trafiają na koniec listy wyikowej. Na przykład: shuffle [3; 2; 8; 1; 9; 3; 6] [5; 7; 0] = [3; 5; 2; 7; 8; 0; 1; 9; 3; 6].
Ćwiczenie [flatten]
Napisz procedurę flatten rozwijającą listę list do listy elementów. Na przykład, flatten [[1; 2]; []; [3; 4; 5]] = [1; 2; 3; 4; 5].
Rozwiązanie
Ćwiczenie [Średnica drzew]
Dana jest definicja typu: type tree = Node of tree * tree | Leaf. Odległością między dwoma wierzchołkami (Node) w drzewie nazywamy minimalną liczbę krawędzi jakie trzeba przejść z jednego wierzchołka do drugiego. Średnicą drzewa nazwiemy maksymalną odległość między dwoma węzłami (Node) w drzewie. Przyjmujemy, że średnica pustego drzewa (Leaf) jest równa 0. Napisz procedurę srednica: tree -> int, która oblicza średnicę danego drzewa.
Rozwiązanie
Ćwiczenie [Drzewa dowolnego stopnia]
Zdefiniuj typ reprezentujący drzewa o wierzchołkach dowolnego (skończonego) stopnia. Zdefiniuj trochę procedur operujących na takich drzewach, np. procedury wyznaczające listy elementów w porządku prefiksowym i postfiksowym.
Laboratorium
Ćwiczenia programistyczne na listy:
Ćwiczenie [-ty element listy]
Zaimplementuj procedurę zwracającą -ty element listy.
Rozwiązanie
Ćwiczenie [Lista ]
Napisz procedurę tworzącą listę pierwszych liczb naturalnych.
Rozwiązanie
Ćwiczenie [append]
Zaimplementuj procedurę append sklejającą listy.
Rozwiązanie
Ćwiczenie [ciąg różnicowy]
Ciąg różnicowy ciągu to ciąg postaci . Napisz procedurę obliczającą ciąg różnicowy żądanej listy liczb całkowitych.
Rozwiązanie
Rozwiązanie 2
Ćwiczenie [ciąg ciągów różnicowych]
Napisz procedurę obliczającą listę kolejnych ciągów różnicowych zadanej listy liczb całkowitych, tzn.: daną listę, jej ciąg różnicowy, ciąg różnicowy ciągu różnicowego itd.
Rozwiązanie
Ćwiczenie [minimum-maksimum]
Napisz procedurę, której wynikiem jest para: minimum i maksimum elementów z listy. Procedura ta powinna wykonywać co najwyżej porównań.
Ćwiczenie [last]
Napisz procedurę last, której wynikiem jest ostatni element (niepustej) listy.
Rozwiązanie
Ćwiczenie [head i tail]
Rozwiązanie
Ćwiczenie [sortowanie]
Zaimplementuj sortowanie: przez scalanie lub quick-sort.
Ćwiczenie [arytmetyka nieograniczonych liczb całkowitych]
Zaimplementuj pakiet arytmetyczny nieograniczonych liczb całkowitych.
Ćwiczenie [lista większościowa]
Rozwiązanie
Ćwiczenie [trójki]
Napisz procedurę trójki:int list -> (int * int * int) list, która dla zadanej listy dodatnich liczb całkowitych, uporządkowanej rosnąco, stworzy listę takich trójek liczb z danej listy, że:
- ,
- liczby , i spełniają nierówność trójkąta, czyli .
Ćwiczenia na inne struktury danych:
Ćwiczenie [arytmetyka liczb wymiernych]
Zaimplementuj arytmetykę liczb wymiernych, reprezentując liczby wymierne jako rekordy złożone z licznika i mianownika. Implementacja może być uproszczona, np. bez skracania ułamków i bez normalizacji znaków.
Ćwiczenie [reprezentacja daty]
Zdefiniuj typ reprezentujący dni tygodnia, miesiące i datę.
Ćwiczenie [dzień tygodnia]
Zdefiniuj procedurę obliczającą na podstawie daty dzień tygodnia. Możesz założyć, że dana jest procedura sylwester, która na podstawie roku określa jakiego dnia tygodnia był Sylwester.
Ćwiczenie [słaby warunek BST]
Napisz procedurę, która dla dowolnego drzewa binarnego poprawia je tak, że spełnia ono słabszą wersję warunku BST: dla dowolnego węzła, lewy syn nie jest większy, a prawy nie jest mniejszy niż węzeł.
Ćwiczenie [wyważanie drzewa BST]
Napisz procedurę, która przekształca dane drzewo binarne w wyważone drzewo binarne, zachowując kolejność elementów w porządku infiksowym.
Ćwiczenie [zbiory jako drzewa BST]
Zaimplementuj takie operacje na zbiorach (reprezentowanych jako drzewa BST) jak suma, przecięcie i różnica.
Ćwiczenie [kolejki dwustronne]
Rozszerz implementację kolejek o wkładanie i wyjmowanie elementów z obydwu stron. Jaką własność powinna zachowywać procedura balance, aby koszt zamortyzowany operacji był stały?
Ćwiczenie [liczby naturalne]
type nat = ZERO | SUCC of nat;;
Zaimplementuj operacje arytmetyczne na takich liczbach.