Wenn Sie die Mindestoperationen eines generischen Computers von Grund auf neu erstellen, steht "Iteration" an erster Stelle als Baustein und ist weniger ressourcenintensiv als "Rekursion". Daher ist ergo schneller.
Wir werden eine Hierarchie von Konzepten erstellen, beginnend bei Null und zunächst die grundlegenden Kernkonzepte definieren und dann mit diesen Konzepte der zweiten Ebene erstellen und so weiter.
Erstes Konzept: Speicherzellen, Speicher, Status . Um etwas zu tun, benötigen Sie Orte zum Speichern der endgültigen und mittleren Ergebniswerte. Nehmen wir an, wir haben ein unendliches Array von "ganzzahligen" Zellen, genannt Speicher , M [0..Infinite].
Anleitung: etwas tun - eine Zelle transformieren, ihren Wert ändern. Zustand ändern . Jede interessante Anweisung führt eine Transformation durch. Grundlegende Anweisungen sind:
a) Speicherzellen setzen und verschieben
- Speichern Sie einen Wert im Speicher, z. B.: Speichern Sie 5 m [4]
- Kopieren Sie einen Wert an eine andere Position: zB: speichern Sie m [4] m [8]
b) Logik und Arithmetik
- und oder xor nicht
- add, sub, mul, div. zB m [7] m [8] hinzufügen
Ein ausführender Agent : ein Kern in einer modernen CPU. Ein "Agent" kann Anweisungen ausführen. Ein Agent kann auch eine Person sein, die dem Algorithmus auf Papier folgt.
Reihenfolge der Schritte: eine Folge von Anweisungen : dh: zuerst ausführen, danach tun usw. Eine zwingende Folge von Anweisungen. Sogar einzeilige Ausdrücke sind "eine zwingende Folge von Anweisungen". Wenn Sie einen Ausdruck mit einer bestimmten "Bewertungsreihenfolge" haben, haben Sie Schritte . Dies bedeutet, dass selbst ein einzelner zusammengesetzter Ausdruck implizite „Schritte“ und eine implizite lokale Variable enthält (nennen wir ihn „Ergebnis“). z.B:
4 + 3 * 2 - 5
(- (+ (* 3 2) 4 ) 5)
(sub (add (mul 3 2) 4 ) 5)
Der obige Ausdruck impliziert 3 Schritte mit einer impliziten "Ergebnis" -Variablen.
// pseudocode
1. result = (mul 3 2)
2. result = (add 4 result)
3. result = (sub result 5)
Selbst Infix-Ausdrücke sind daher eine zwingende Folge von Anweisungen , da Sie eine bestimmte Auswertungsreihenfolge haben . Der Ausdruck impliziert eine Folge von Operationen, die in einer bestimmten Reihenfolge ausgeführt werden müssen, und da es Schritte gibt, gibt es auch eine implizite Zwischenvariable "Ergebnis".
Anweisungszeiger : Wenn Sie eine Folge von Schritten haben, haben Sie auch einen impliziten "Anweisungszeiger". Der Befehlszeiger markiert den nächsten Befehl und rückt vor, nachdem der Befehl gelesen wurde, aber bevor der Befehl ausgeführt wird.
In dieser Pseudo-Rechenmaschine ist der Befehlszeiger Teil des Speichers . (Hinweis: Normalerweise ist der Befehlszeiger ein "spezielles Register" in einem CPU-Kern, aber hier werden wir die Konzepte vereinfachen und davon ausgehen, dass alle Daten (einschließlich Register) Teil von "Speicher" sind.)
Springen - Sobald Sie eine geordnete Anzahl von Schritten und einen Anweisungszeiger haben , können Sie die Anweisung " Speichern " anwenden , um den Wert des Anweisungszeigers selbst zu ändern. Wir werden diese spezielle Verwendung der Speicheranweisung mit einem neuen Namen bezeichnen: Jump . Wir verwenden einen neuen Namen, weil es einfacher ist, ihn als neues Konzept zu betrachten. Durch Ändern des Anweisungszeigers weisen wir den Agenten an, „mit Schritt x fortzufahren“.
Unendliche Iteration : Durch Zurückspringen können Sie den Agenten jetzt dazu bringen, eine bestimmte Anzahl von Schritten zu "wiederholen". Zu diesem Zeitpunkt haben wir eine unendliche Iteration.
1. mov 1000 m[30]
2. sub m[30] 1
3. jmp-to 2 // infinite loop
Bedingt - Bedingte Ausführung von Anweisungen. Mit der "bedingten" Klausel können Sie eine von mehreren Anweisungen basierend auf dem aktuellen Status (der mit einer vorherigen Anweisung festgelegt werden kann) bedingt ausführen.
Richtige Iteration : Mit der Bedingungsklausel können wir nun der Endlosschleife der Rücksprunganweisung entkommen . Wir haben jetzt eine bedingte Schleife und dann die richtige Iteration
1. mov 1000 m[30]
2. sub m[30] 1
3. (if not-zero) jump 2 // jump only if the previous
// sub instruction did not result in 0
// this loop will be repeated 1000 times
// here we have proper ***iteration***, a conditional loop.
Benennen : Geben Sie einem bestimmten Speicherort Namen, der Daten oder einen Schritt enthält . Dies ist nur eine "Bequemlichkeit" zu haben. Wir fügen keine neuen Anweisungen hinzu, indem wir "Namen" für Speicherorte definieren können. "Benennen" ist keine Anweisung für den Agenten, sondern nur eine Annehmlichkeit für uns. Durch die Benennung ist Code (an dieser Stelle) leichter zu lesen und leichter zu ändern.
#define counter m[30] // name a memory location
mov 1000 counter
loop: // name a instruction pointer location
sub counter 1
(if not-zero) jmp-to loop
Einstufiges Unterprogramm : Angenommen, Sie müssen eine Reihe von Schritten häufig ausführen. Sie können die Schritte an einer benannten Position im Speicher speichern und dann zu dieser Position springen , wenn Sie sie ausführen müssen (Aufruf). Am Ende der Sequenz werden Sie brauchen Rückkehr zum Punkt des Aufrufs Ausführung fortzusetzen. Mit diesem Mechanismus erstellen Sie neue Anweisungen (Unterprogramme), indem Sie Kernanweisungen erstellen .
Implementierung: (keine neuen Konzepte erforderlich)
- Speichern Sie den aktuellen Anweisungszeiger an einer vordefinierten Speicherposition
- zum Unterprogramm springen
- Am Ende der Unterroutine rufen Sie den Anweisungszeiger aus dem vordefinierten Speicherort ab und springen effektiv zur folgenden Anweisung des ursprünglichen Aufrufs zurück
Problem mit der einstufigen Implementierung: Sie können keine andere Unterroutine von einer Unterroutine aus aufrufen. In diesem Fall überschreiben Sie die Rücksprungadresse (globale Variable), sodass Sie keine Anrufe verschachteln können.
Um eine bessere Implementierung für Unterprogramme zu haben: Sie benötigen einen STACK
Stapel : Sie definieren einen Speicherplatz als "Stapel", Sie können Werte auf dem Stapel "pushen" und auch den letzten "gepussten" Wert "platzen". Um einen Stapel zu implementieren Sie benötigen Stapelzeiger (ähnlich dem Instruction Pointer) , die Punkte auf den tatsächlichen „Kopf“ des Stapels. Wenn Sie einen Wert „pushen“, wird der Stapelzeiger dekrementiert und Sie speichern den Wert. Wenn Sie "pop", erhalten Sie den Wert am tatsächlichen Stapelzeiger und dann wird der Stapelzeiger erhöht.
Unterprogramme Jetzt, da wir einen Stapel haben, können wir geeignete Unterprogramme implementieren, die verschachtelte Aufrufe ermöglichen . Die Implementierung ist ähnlich, aber anstatt den Befehlszeiger an einer vordefinierten Speicherposition zu speichern, "pushen" wir den Wert der IP im Stapel . Am Ende der Unterroutine „platzen“ wir einfach den Wert vom Stapel und springen nach dem ursprünglichen Aufruf effektiv zur Anweisung zurück . Diese Implementierung mit einem „Stapel“ ermöglicht das Aufrufen einer Unterroutine von einer anderen Unterroutine. Mit dieser Implementierung können wir mehrere Abstraktionsebenen erstellen, wenn wir neue Anweisungen als Unterroutinen definieren, indem wir Kernanweisungen oder andere Unterroutinen als Bausteine verwenden.
Rekursion : Was passiert, wenn sich ein Unterprogramm selbst aufruft? Dies wird als "Rekursion" bezeichnet.
Problem: Überschreiben der lokalen Zwischenergebnisse, die eine Unterroutine im Speicher speichern kann. Da Sie dieselben Schritte aufrufen / wiederverwenden, werden die Zwischenergebnisse , wenn sie an vordefinierten Speicherorten (globalen Variablen) gespeichert sind, bei den verschachtelten Aufrufen überschrieben.
Lösung: Um eine Rekursion zu ermöglichen, sollten Unterprogramme lokale Zwischenergebnisse im Stapel speichern. Daher werden die Zwischenergebnisse bei jedem rekursiven Aufruf (direkt oder indirekt) an verschiedenen Speicherorten gespeichert.
...
Beachten Sie schließlich, dass Sie viele Möglichkeiten haben, die Rekursion zu verwenden. Sie haben überall rekursive Datenstrukturen , Sie sehen sich jetzt eine an: Teile des DOM, die das unterstützen, was Sie lesen, sind ein RDS, ein JSON-Ausdruck ist ein RDS, das hierarchische Dateisystem in Ihrem Computer ist ein RDS, dh: Sie haben ein Stammverzeichnis, das Dateien und Verzeichnisse enthält, jedes Verzeichnis, das Dateien und Verzeichnisse enthält, jedes dieser Verzeichnisse, das Dateien und Verzeichnisse enthält ...