Paradygmaty programowania/Ćwiczenia 11: Programowanie funkcyjne w Haskellu II: Różnice pomiędzy wersjami
(Nie pokazano 4 pośrednich wersji utworzonych przez tego samego użytkownika) | |||
Linia 19: | Linia 19: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Spróbujmy stworzyć ciąg par, w których pierwszy element to kolejne liczby naturalne, a drugi to kolejne silnie. A zatem można tak: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Spróbujmy stworzyć ciąg par, w których pierwszy element to kolejne liczby naturalne, a drugi to kolejne silnie. A zatem można tak: | ||
silnia :: Integer -> Integer | |||
silnia = snd . rekdef g (0, 1) | silnia = snd . rekdef g (0, 1) | ||
where g (x, y) = (x + 1, (x + 1)*y) | where g (x, y) = (x + 1, (x + 1)*y) | ||
Zauważmy, że stosujemy tu ponownie technikę „łączenia w pary”. | Zauważmy, że stosujemy tu ponownie technikę „łączenia w pary”. | ||
Linia 29: | Linia 29: | ||
Poniższa funkcja ma sprawdzać, czy podana lista jest pusta. Dlaczego definicja ta jest niepoprawna? | Poniższa funkcja ma sprawdzać, czy podana lista jest pusta. Dlaczego definicja ta jest niepoprawna? | ||
pusta1 :: [a] -> Bool | |||
pusta1 x = (x == []) | pusta1 x = (x == []) | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Konieczna jest klauzula ograniczająca typ a do klasy Eq, by Haskell zgodził się na użycie operatora ==, określonego przecież dla typów z klasy Eq. Sygnatura powinna zatem wyglądać tak: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Konieczna jest klauzula ograniczająca typ a do klasy Eq, by Haskell zgodził się na użycie operatora ==, określonego przecież dla typów z klasy Eq. Sygnatura powinna zatem wyglądać tak: | ||
pusta1 :: Eq a => [a] -> Bool | |||
</div></div> | </div></div> | ||
Linia 40: | Linia 40: | ||
Rozważmy zatem inny wariant sprawdzania pustości listy. Wykorzystajmy funkcję, która wylicza długość listy: | Rozważmy zatem inny wariant sprawdzania pustości listy. Wykorzystajmy funkcję, która wylicza długość listy: | ||
pusta2 :: [a] -> Bool | |||
pusta2 x = (dl x == 0) | pusta2 x = (dl x == 0) | ||
Ta definicja jest poprawna, ma jednak poważną wadę. Jaką? Jak ją wyeliminować, tzn. jak napisać dobrą funkcję sprawdzającą pustość listy? | Ta definicja jest poprawna, ma jednak poważną wadę. Jaką? Jak ją wyeliminować, tzn. jak napisać dobrą funkcję sprawdzającą pustość listy? | ||
Linia 47: | Linia 47: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Sprawdzenie pustości listy jest tu poprzedzone wyliczeniem jej długości — a to przecież jest strata czasu. Co gorsza, takie sprawdzenie nie zadziała dla list nieskończonych, np. [1..]. Dobra funkcja może wyglądać jak poniżej; nie czynimy tu żadnych założeń o typie a ani nie liczymy długości listy. | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Sprawdzenie pustości listy jest tu poprzedzone wyliczeniem jej długości — a to przecież jest strata czasu. Co gorsza, takie sprawdzenie nie zadziała dla list nieskończonych, np. [1..]. Dobra funkcja może wyglądać jak poniżej; nie czynimy tu żadnych założeń o typie a ani nie liczymy długości listy. | ||
pusta3 :: [a] -> Bool | |||
pusta3 [] = True | pusta3 [] = True | ||
pusta3 (_:_) = False | pusta3 (_:_) = False | ||
</div></div> | </div></div> | ||
Linia 64: | Linia 64: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> To jest proste: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> To jest proste: | ||
połącz :: <nowiki>[[a]]</nowiki> -> [a] | |||
połącz [] = [] | połącz [] = [] | ||
połącz (x:xs) = x ++ połącz xs | połącz (x:xs) = x ++ połącz xs | ||
</div></div> | </div></div> | ||
===Zadanie 7=== | ===Zadanie 7=== | ||
Przy funkcji połącz zdefiniowanej tak jak w poprzednim zadaniu, które wywołania są poprawne? | Przy funkcji połącz zdefiniowanej tak jak w poprzednim zadaniu, które wywołania są poprawne? | ||
* połącz [[[]]] | * połącz <nowiki>[[[]]]</nowiki> | ||
* połącz [[1, 2], [], [3]] | * połącz <nowiki>[[1, 2], [], [3]]</nowiki> | ||
* połącz [[[1, 2]], [], [[3]]] | * połącz <nowiki>[[[1, 2]], [], [[3]]]</nowiki> | ||
* połącz [[[1, 2]], [[]], [[3]]] | * połącz <nowiki>[[[1, 2]], [[]], [[3]]]</nowiki> | ||
* połącz [[], [[]], [[[]]]] | * połącz <nowiki>[[], [[]], [[[]]]]</nowiki> | ||
* połącz [[1], [[2]], [[[3]]]] | * połącz <nowiki>[[1], [[2]], [[[3]]]]</nowiki> | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Wszystkie z wyjątkiem ostatniego. We wszystkich bowiem przypadkach poza ostatnim Haskell jest w stanie dobrać taki typ a, żeby uzyskać zgodny z sygnaturą typ parametru [[a]]. Przykładowo, w dwóch środkowych wywołaniach a to [Integer]. Pouczające jest obejrzenie wyników wszystkich tych wywołań... | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Wszystkie z wyjątkiem ostatniego. We wszystkich bowiem przypadkach poza ostatnim Haskell jest w stanie dobrać taki typ a, żeby uzyskać zgodny z sygnaturą typ parametru <nowiki>[[a]]</nowiki>. Przykładowo, w dwóch środkowych wywołaniach a to [Integer]. Pouczające jest obejrzenie wyników wszystkich tych wywołań... | ||
</div></div> | </div></div> |
Aktualna wersja na dzień 22:00, 23 wrz 2006
Zadanie 1
Zdefiniować odejmowanie dla typu Nat w dwóch wariantach: zupełnym i częściowym. W pierwszym odjęcie większej liczby od mniejszej ma dawać zero. W drugim wartość taka ma być niezdefiniowana. Co się stanie, jeśli spróbujemy jednak odjąć większą liczbę od mniejszej?
Zadanie 2
Zdefiniować silnię jako stosowną aplikację funkcji rekdef.
Zadanie 3*
Poniższa funkcja ma sprawdzać, czy podana lista jest pusta. Dlaczego definicja ta jest niepoprawna?
pusta1 :: [a] -> Bool pusta1 x = (x == [])
Zadanie 4
Rozważmy zatem inny wariant sprawdzania pustości listy. Wykorzystajmy funkcję, która wylicza długość listy:
pusta2 :: [a] -> Bool pusta2 x = (dl x == 0)
Ta definicja jest poprawna, ma jednak poważną wadę. Jaką? Jak ją wyeliminować, tzn. jak napisać dobrą funkcję sprawdzającą pustość listy?
Zadanie 5
Napisać funkcję, która zwraca ostatni element niepustej listy. Funkcja powinna działać dla list dowolnego typu, bez jakichkolwiek dodatkowych założeń.
Zadanie 6
Zdefiniować funkcję połącz, która połączy listę list w jedną listę. Przykładowo, połącz [[1, 2], [3, 4, 5], [6]] ma dać wynik [1, 2, 3, 4, 5, 6].
Zadanie 7
Przy funkcji połącz zdefiniowanej tak jak w poprzednim zadaniu, które wywołania są poprawne?
- połącz [[[]]]
- połącz [[1, 2], [], [3]]
- połącz [[[1, 2]], [], [[3]]]
- połącz [[[1, 2]], [[]], [[3]]]
- połącz [[], [[]], [[[]]]]
- połącz [[1], [[2]], [[[3]]]]