Paradygmaty programowania/Ćwiczenia 10: Programowanie funkcyjne w Haskellu I: Różnice pomiędzy wersjami
Nie podano opisu zmian |
|||
(Nie pokazano 7 wersji utworzonych przez 3 użytkowników) | |||
Linia 4: | Linia 4: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Można klasycznie: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Można klasycznie: | ||
p5 :: Integer -> Integer | |||
p5 x = x + 5 | p5 x = x + 5 | ||
Można też bardziej w duchu języka funkcyjnego: | Można też bardziej w duchu języka funkcyjnego: | ||
p5 :: Integer -> Integer | |||
p5 = (+5) | p5 = (+5) | ||
Zauważmy, że w tym drugim przypadku funkcję definiujemy nie na elementach, lecz na poziomie funkcji. | Zauważmy, że w tym drugim przypadku funkcję definiujemy nie na elementach, lecz na poziomie funkcji. | ||
Linia 18: | Linia 18: | ||
Przypomnijmy definicję funkcji mnożącej dwie liczby: | Przypomnijmy definicję funkcji mnożącej dwie liczby: | ||
mnóż :: (Integer, Integer) -> Integer | |||
mnóż (x, y) = if x == 0 || y == 0 then 0 else x * y | mnóż (x, y) = if x == 0 || y == 0 then 0 else x * y | ||
Czy poniższy wariant byłby lepszy pod względem wykorzystania mechanizmu obliczania leniwego (tzn. który argument i kiedy nie będzie obliczany)? | Czy poniższy wariant byłby lepszy pod względem wykorzystania mechanizmu obliczania leniwego (tzn. który argument i kiedy nie będzie obliczany)? | ||
mnóż (x, y) = if x == 0 then 0 else x * y | |||
===Zadanie 3=== | ===Zadanie 3=== | ||
Która z poniższych funkcji nie jest jednoargumentowa? | Która z poniższych funkcji nie jest jednoargumentowa? | ||
(+2), (–7), (*5), (/2), (3/), (<1), (`div` 3), (100 `div`) | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Wyjątkowo traktowany jest minus, w związku z czym (–7) jest traktowane nie jako jednoargumentowa funkcja odejmująca 7, lecz po prostu jako liczba –7. | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Wyjątkowo traktowany jest minus, w związku z czym (–7) jest traktowane nie jako jednoargumentowa funkcja odejmująca 7, lecz po prostu jako liczba –7. | ||
Linia 44: | Linia 44: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Argument funkcji error to napis (typ String), natomiast wynik musi pasować do dowolnego kontekstu — jest to zatem niczym nie ograniczony typ polimorficzny, wyrażony zmienną typową bez klauzuli ograniczającej zakres. Summa summarum: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Argument funkcji error to napis (typ String), natomiast wynik musi pasować do dowolnego kontekstu — jest to zatem niczym nie ograniczony typ polimorficzny, wyrażony zmienną typową bez klauzuli ograniczającej zakres. Summa summarum: | ||
error :: String -> a | |||
</div></div> | </div></div> | ||
Linia 50: | Linia 50: | ||
Postawmy definicję: | Postawmy definicję: | ||
para :: Num a => a -> (a, a) | |||
para x = (x, x) | para x = (x, x) | ||
Funkcję podnoszącą do kwadratu można teraz wyrazić jako złożenie powyższej funkcji tworzącej parę z mnożeniem. Czy zatem poniższa definicja jest poprawna? Jeśli nie, to co należy zmienić? | Funkcję podnoszącą do kwadratu można teraz wyrazić jako złożenie powyższej funkcji tworzącej parę z mnożeniem. Czy zatem poniższa definicja jest poprawna? Jeśli nie, to co należy zmienić? | ||
kw :: Num a => a -> a | |||
kw = (*) . para | kw = (*) . para | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Problem w tym, że zwykły operator mnożenia (sama gwiazdka) wymaga zapisu infiksowego, zaś (*) to postać rozwinięta. Tu natomiast potrzebne jest mnożenie w postaci zwykłej funkcji dwuargumentowej. Trzeba zatem „zwinąć” funkcję (*), pisząc: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Problem w tym, że zwykły operator mnożenia (sama gwiazdka) wymaga zapisu infiksowego, zaś (*) to postać rozwinięta. Tu natomiast potrzebne jest mnożenie w postaci zwykłej funkcji dwuargumentowej. Trzeba zatem „zwinąć” funkcję (*), pisząc: | ||
kw = uncurry (*) . para | |||
</div></div> | </div></div> | ||
===Zadanie 8=== | ===Zadanie 8=== | ||
Napisz funkcję ppd | Napisz funkcję ppd(n, x), która podnosi x do potęgi <math>2^n</math>. Zwróć uwagę zarówno na efektywność, jak i na przejrzystość definicji. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Podnoszenie do potęgi dwójki pozwala napisać szczególnie urodziwą funkcję... Korzystając z wcześniej zdefiniowanych funkcji możemy napisać: | <div class="mw-collapsible mw-made=collapsible mw-collapsed">'''Wskazówka:''' <div class="mw-collapsible-content" style="display:none"> Podnoszenie do potęgi dwójki pozwala napisać szczególnie urodziwą funkcję... Korzystając z wcześniej zdefiniowanych funkcji możemy napisać: | ||
ppd :: Num a => (Integer, a) -> a | |||
ppd (n, x) = iter (n, kw) x | ppd (n, x) = iter (n, kw) x | ||
Decydując się na wariant rozwinięty, moglibyśmy zapisać „jeszcze bardziej funkcyjną” definicję: | Decydując się na wariant rozwinięty, moglibyśmy zapisać „jeszcze bardziej funkcyjną” definicję: | ||
ppdr :: Num a => Integer -> a -> a | |||
ppdr n = iter (n, kw) | ppdr n = iter (n, kw) | ||
</div></div> | </div></div> |
Aktualna wersja na dzień 21:35, 23 wrz 2006
Zadanie 1
Zdefiniuj na dwa sposoby funkcję, która do danej liczby całkowitej dodaje 5.
Zadanie 2
Przypomnijmy definicję funkcji mnożącej dwie liczby:
mnóż :: (Integer, Integer) -> Integer mnóż (x, y) = if x == 0 || y == 0 then 0 else x * y
Czy poniższy wariant byłby lepszy pod względem wykorzystania mechanizmu obliczania leniwego (tzn. który argument i kiedy nie będzie obliczany)?
mnóż (x, y) = if x == 0 then 0 else x * y
Zadanie 3
Która z poniższych funkcji nie jest jednoargumentowa?
(+2), (–7), (*5), (/2), (3/), (<1), (`div` 3), (100 `div`)
Zadanie 4
Napisz kilka wariantów „klasycznej funkcji dydaktycznej”, czyli silni. Zadbaj o obsługę błędnych argumentów. Haskell zawiera standardową funkcję error, która powoduje zerwanie obliczeń i wyświetlenie komunikatu o błędzie. Funkcja error bierze jeden argument napisowy. Zwróć uwagę, że aby wywołać funkcję z argumentem ujemnym, trzeba go ująć w nawiasy (w przeciwnym razie minus w wywołaniu np. silnia –2 zostanie uznany za operator dwuargumentowy i dostaniemy komunikat o błędzie, ale nie tym, o którym mówimy...).
Zadanie 5*
Zastanów się (ponownie...) nad obliczaniem wyrazów ciągu Fibonacciego. Z oczywistych powodów prosta funkcja, odzwierciedlająca rekurencyjną definicję, jest nieefektywna. Czy wiesz już, jak napisać lepszą?
Zadanie 6
Zapisz nową wersję funkcji rkwadrat, która wylicza pierwiastki równania kwadratowego. Nowa wersja powinna zgłaszać błąd, jeśli równanie nie ma pierwiastków rzeczywistych. Jak sądzisz, jaka jest sygnatura funkcji error?
Zadanie 7
Postawmy definicję:
para :: Num a => a -> (a, a) para x = (x, x)
Funkcję podnoszącą do kwadratu można teraz wyrazić jako złożenie powyższej funkcji tworzącej parę z mnożeniem. Czy zatem poniższa definicja jest poprawna? Jeśli nie, to co należy zmienić?
kw :: Num a => a -> a kw = (*) . para
Zadanie 8
Napisz funkcję ppd(n, x), która podnosi x do potęgi . Zwróć uwagę zarówno na efektywność, jak i na przejrzystość definicji.