Dies ist eine schöne Übung, um die Programmiersprache, die Sie lernen wollten, fließender zu beherrschen, aber nur leicht herumgebastelt haben. Dies beinhaltet das Arbeiten mit Objekten, das Verwenden oder Simulieren von Verschlüssen und das Dehnen des Typsystems.
Ihre Aufgabe ist es, Code zum Verwalten von Lazy Lists zu schreiben und dann diesen Algorithmus zum Generieren von Fibonacci-Zahlen zu implementieren:
Codebeispiele sind in Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Ergebnis:
[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]
Ihre Lazy-List-Implementierung sollte diese Richtlinien erfüllen:
- Ein Listenknoten ist eines von drei Dingen:
- Nil - Leere Liste.
[]
- Nachteile - Ein einzelnes Element, gepaart mit einer Liste der verbleibenden Elemente:
1 : [2,3,4,5]
(:
ist der Nachteile-Operator in Haskell) - Thunk - Eine verzögerte Berechnung, die bei Bedarf einen List-Knoten erzeugt.
- Nil - Leere Liste.
- Es unterstützt die folgenden Operationen:
- nil - Erstellt eine leere Liste.
- cons - Konstruiert eine Cons-Zelle.
- thunk - Konstruiert einen Thunk mit einer Funktion, die keine Argumente akzeptiert und ein Nil oder Cons zurückgibt.
- force - Bei gegebenem Listenknoten:
- Wenn es sich um einen Nullwert oder einen Nachteil handelt, geben Sie ihn einfach zurück.
- Wenn es ein Thunk ist, rufen Sie seine Funktion auf, um eine Null oder Nachteile zu erhalten. Ersetzen Sie den Thunk durch diesen Nil oder Cons und geben Sie ihn zurück.
Hinweis: Das Ersetzen des Thunks durch seinen erzwungenen Wert ist ein wichtiger Bestandteil der Definition von "faul" . Wenn dieser Schritt übersprungen wird, ist der obige Fibonacci-Algorithmus zu langsam.
- leer - Überprüfen Sie, ob ein List-Knoten Null ist (nachdem er erzwungen wurde).
- head (aka "car") - Liefert den ersten Eintrag einer Liste (oder wirft einen Fit, wenn es Nil ist).
- tail (aka "cdr") - Liefert die Elemente nach dem Kopf einer Liste (oder wirft einen Fit, wenn es Nil ist).
- zipWith - Wenden Sie die Funktion bei einer Binärfunktion (z. B.
(+)
) und zwei (möglicherweise unendlichen) Listen auf die entsprechenden Elemente der Listen an. Beispiel:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Nimm die ersten N Elemente der Liste, wenn du eine Nummer N und eine (möglicherweise unendliche) Liste hast.
- Drucken - Alle Elemente in einer Liste drucken. Dies sollte schrittweise funktionieren, wenn eine lange oder unendliche Liste angegeben wird.
fibs
verwendet sich in seiner eigenen Definition. Das Einrichten einer faulen Rekursion ist etwas schwierig. Sie müssen so etwas tun:- Vergeben Sie einen Thunk für
fibs
. Lassen Sie es vorerst in einem Dummy-Zustand. - Definieren Sie die Thunk-Funktion, die von einem Verweis auf abhängt
fibs
. - Aktualisieren Sie den Thunk mit seiner Funktion.
Sie können dieses Installationsprogramm ausblenden, indem Sie eine Funktion definieren,
fix
die eine Listenrückgabefunktion mit einem eigenen Rückgabewert aufruft. Erwägen Sie, ein kurzes Nickerchen zu machen, damit diese Idee greifbar wird.- Vergeben Sie einen Thunk für
Polymorphismus (die Fähigkeit, mit Listen jeglicher Art von Gegenständen zu arbeiten) ist nicht erforderlich, aber versuchen Sie, einen Weg zu finden, der in Ihrer Sprache idiomatisch ist.
- Sorgen Sie sich nicht um die Speicherverwaltung. Sogar Sprachen mit Garbage Collection neigen dazu, Objekte mit sich herumzutragen, die Sie nie wieder verwenden werden (z. B. auf dem Aufrufstapel). Seien Sie also nicht überrascht, wenn Ihr Programm beim Durchlaufen einer unendlichen Liste Speicherplatz verliert.
Sie können leicht von diesen Richtlinien abweichen, um die Besonderheiten Ihrer Sprache zu berücksichtigen oder alternative Ansätze für faule Listen zu untersuchen.
Regeln:
- Wählen Sie eine Sprache, die Sie nicht gut kennen. Ich kann dies nicht "fordern", daher das Tag "honor-system". Die Wähler können jedoch Ihren Verlauf überprüfen, um festzustellen, in welchen Sprachen Sie Beiträge verfasst haben.
Verwenden Sie nicht die in Ihrer Sprache integrierte Lazy-List-Unterstützung, um alles zu erledigen. Schreibe etwas Wesentliches oder zumindest Interessantes.
Haskell ist ziemlich out. Das heißt, es sei denn, Sie machen so etwas:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Hinweis: Die nicht strenge Bewertung von Haskell ist nicht uneingeschränkt zulässig, aber Ihre Implementierung einer verzögerten Liste sollte ihre Funktionalität nicht direkt von dort ableiten. In der Tat wäre es interessant, eine effiziente, rein funktionale Lösung zu sehen, die keine Faulheit erfordert.
Python:
- Verwenden Sie keine itertools.
- Generatoren sind in Ordnung, aber wenn Sie sie verwenden, müssen Sie einen Weg finden, um erzwungene Werte zu speichern.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Für den obigen Fibonacci-Algorithmus spielt dies jedoch keine Rolle, da beide Argumente für zipWith unendliche Listen sind.
fibs
richtig zu implementieren , da es von sich selbst abhängt. Ich habe die Frage aktualisiert, um auf die faulen Rekursionen einzugehen. FUZxxl hat es von ihm / ihr / sich selbst herausgefunden.
zipWith
von zwei unterschiedlich langen Listen verhalten ?