Jest to fajne ćwiczenie, dzięki któremu można płynniej posługiwać się tym językiem programowania, którego zamierzałeś się uczyć, ale majstrowałeś tylko lekko. Obejmuje to pracę z obiektami, używanie lub symulowanie zamknięć i rozciąganie układu czcionek.
Twoim zadaniem jest napisanie kodu do zarządzania listami leniwymi, a następnie użycie go do zaimplementowania tego algorytmu do generowania liczb Fibonacciego:
Próbki kodu znajdują się w Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Wynik:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
Implementacja listy leniwej powinna spełniać następujące wytyczne:
- Węzeł listy jest jedną z trzech rzeczy:
- Zero - pusta lista.
[]
- Wady - Pojedynczy element, w połączeniu z listą pozostałych elementów:
1 : [2,3,4,5]
(:
jest operatorem wad w Haskell) - Thunk - odroczone obliczenia, które w razie potrzeby tworzą węzeł List.
- Zero - pusta lista.
- Obsługuje następujące operacje:
- zero - Zbuduj pustą listę.
- Minusy - Zbuduj komórkę przeciw.
- thunk - Zbuduj Thunk, biorąc pod uwagę funkcję, która nie przyjmuje argumentów i zwraca zero lub minus.
- force - Biorąc pod uwagę węzeł listy:
- Jeśli jest to zero lub minus, po prostu go zwróć.
- Jeśli jest to Thunk, wywołaj jego funkcję, aby uzyskać zero lub minusy. Wymień thunk na zero lub minusy i zwróć go.
Uwaga: Zastąpienie thunk jego wymuszoną wartością jest ważną częścią definicji „leniwy” . Jeśli ten krok zostanie pominięty, powyższy algorytm Fibonacciego będzie zbyt wolny.
- puste - Sprawdź, czy węzeł listy ma wartość zero (po wymuszeniu).
- head (aka „car”) - Zdobądź pierwszą pozycję na liście (lub rzuć dopasowanie, jeśli to zero).
- tail (aka „cdr”) - Pobierz elementy za nagłówkiem listy (lub rzuć pas, jeśli jest zero).
- zipWith - Biorąc pod uwagę funkcję binarną (np.
(+)
) i dwie (być może nieskończone) listy, zastosuj funkcję do odpowiednich pozycji list. Przykład:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Biorąc pod uwagę liczbę N i listę (być może nieskończoną), weź pierwsze N elementów listy.
- print - Wydrukuj wszystkie elementy z listy. Powinno to działać przyrostowo, jeśli podano długą lub nieskończoną listę.
fibs
używa się w swojej własnej definicji. Konfigurowanie leniwej rekurencji jest nieco trudne; musisz zrobić coś takiego:- Przydziel thunk za
fibs
. Na razie pozostaw to w stanie manekina. - Zdefiniuj funkcję thunk, która zależy od odwołania do
fibs
. - Zaktualizuj thunk z jego funkcją.
Możesz ukryć tę hydraulikę, definiując funkcję,
fix
która wywołuje funkcję zwracającą listę z własną wartością zwracaną. Rozważ krótką drzemkę, aby ten pomysł mógł się wprowadzić.- Przydziel thunk za
Polimorfizm (zdolność do pracy z listami dowolnego rodzaju przedmiotów) nie jest wymagany, ale sprawdź, czy możesz znaleźć sposób, aby to zrobić, idiomatyczne w twoim języku.
- Nie martw się o zarządzanie pamięcią. Nawet języki z odśmiecaniem mają tendencję do przenoszenia obiektów, których już nigdy nie użyjesz (np. Na stosie wywołań), więc nie zdziw się, jeśli twój program wycieknie pamięć podczas przeglądania nieskończonej listy.
Zachęcamy do nieznacznego odstępstwa od tych wytycznych, aby uwzględnić szczegóły dotyczące Twojego języka, lub do poszukiwania alternatywnych podejść do leniwych list.
Zasady:
- Wybierz język, którego nie znasz dobrze. Nie mogę tego „wymagać”, stąd tag „honor-system”. Głosujący mogą jednak sprawdzić Twoją historię, aby zobaczyć, w jakich językach piszesz.
Nie używaj wbudowanej obsługi leniwych list w swoim języku do robienia wszystkiego. Zamieść coś znaczącego lub co najmniej interesującego.
Haskell jest prawie nieobecny. To znaczy, chyba że zrobisz coś takiego:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Uwaga: nie ścisła ocena Haskell nie jest niedostępna, ale implementacja leniwej listy nie powinna bezpośrednio czerpać z niej możliwości. W rzeczywistości interesujące byłoby stworzenie wydajnego, czysto funkcjonalnego rozwiązania, które nie wymaga lenistwa.
Pyton:
- Nie używaj narzędzi itertools.
- Generatory są w porządku, ale korzystasz z nich, musisz znaleźć sposób na zapamiętanie wartości wymuszonych.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Nie ma to jednak znaczenia dla powyższego algorytmu Fibonacciego, ponieważ oba argumenty zipWith są nieskończonymi listami.
fibs
poprawnie go wdrożyć , ponieważ zależy to od niego samego. Zaktualizowałem pytanie, aby rozwinąć leniwą rekurencję. FUZxxl sam to zrozumiał.
zipWith
dwóch list o różnych długościach?