Ich habe diesen Code im Handbuch gefunden An Introduction to Programming in Emacs Lisp
, das die Rekursion mit Hilfe der cond
Funktion demonstriert , um die Anzahl der Kieselsteine basierend auf der eingegebenen Anzahl der Zeilen zu ermitteln, dh wenn Zeilen = 2, sollten die Kiesel 3 sein, wenn 4 Zeilen, dann sollten es 10 Kieselsteine sein Dort.
(defun triangle-using-cond (number)
(cond ((<= number 0) 0)
((= number 1) 1)
((> number 1)
(+ number (triangle-using-cond (1- number))))))
bewerte bis 10 nachdem du Argument 4 bestanden hast:
(triangle-using-cond 4)
Das Handbuch hat nicht klar erklärt, was bei jedem Schritt in diesem Beispielcode passiert, und ich konnte nicht herausfinden, wie die Rekursion hier funktioniert. Können Sie mir bitte helfen, die Mechanik Schritt für Schritt zu verstehen, was in jedem Fall passiert?
triangle-using-cond
wobei das Argument 1 kleiner ist als die Zahl. Die Bedingungen gehen in der Reihenfolge von a, b und dann c - was zuerst passt, ist, wo der Bock aufhört.1-4 = 3
. Nun wird der rekursive Aufruf sein(triangle-using-cond 3)
, aber das wird immer wieder mit demselben rekursiven Aufruf enden, bis er die 1-Bedingung erfüllt, richtig? Was wird als nächstes passieren?(triangle-using-cond 3)
?1-
hat einen besonders irreführenden Namen, insbesondere wenn Sie einen Anruf so lesen, als wäre er eine Infixnotation. Es gibt das Argument minus eins zurück. NICHT eins minus das Argument.Antworten:
Verwendung von "printf debugging"
Sie können sich von Emacs verständigen lassen, indem Sie die Funktionsdefinition ändern:
Fügen Sie einfach
(message ...)
irgendwo hinzu, um eine Spur in den*Messages*
Puffer zu drucken .Verwenden von Edebug
Platzieren Sie den Punkt irgendwo in der Funktionsdefinition und drücken Sie, um ihn
C-u C-M-x
zu "instrumentieren". Bewerten Sie dann die Funktion, indem Sie z. B. einen Punkt nach dem anderen platzieren(triangle-using-cond 3)
und auf den anderen schlagenC-x C-e
.Sie befinden sich jetzt im Edebug-Modus. Drücke die Leertaste, um die Funktion zu durchlaufen. Die Zwischenwerte der einzelnen Ausdrücke werden im Echobereich angezeigt. Um den Edebug-Modus zu verlassen, drücken Sie einfach
q
. Um die Instrumentierung zu entfernen, platzieren Sie den Punkt an einer beliebigen Stelle in der Definition und drücken SieC-M-x
, um die Definition neu zu bewerten.Verwendung des Standard-Emacs-Debuggers
M-x debug-on-entry triangle-using-cond
Wenntriangle-using-cond
Sie dann aufgerufen werden, werden Sie in den Emacs-Debugger (Puffer*Backtrace*
) versetzt.Durchlaufen Sie die Bewertung mit
d
(oderc
, um uninteressante Bewertungen zu überspringen).Den Zwischenzustand (variable Werte usw.) können Sie jederzeit
e
anzeigen. Sie werden aufgefordert, ein Geschlecht für die Bewertung einzugeben, und das Bewertungsergebnis wird gedruckt.Bewahren Sie während der Verwendung des Debuggers eine Kopie des Quellcodes in einem anderen Frame auf, damit Sie verfolgen können, was vor sich geht.
Sie können auch explizite Aufrufe einfügen, um den Debugger (mehr oder weniger Haltepunkte) an beliebigen Stellen im Quellcode einzugeben. Sie fügen
(debug)
oder ein(debug nil SOME-SEXP-TO-EVALUATE)
. Im letzteren Fall wird bei Eingabe des DebuggersSOME-SEXP-TO-EVALUATE
ausgewertet und das Ergebnis ausgedruckt. (Denken Sie daran, dass Sie diesen Code in den Quellcode einfügen undC-M-x
zur Auswertung verwenden können. Machen Sie dies dann rückgängig - Sie müssen die bearbeitete Datei nicht speichern.)Using Debugger
Weitere Informationen finden Sie im Elisp-Handbuch, Knoten .Rekursion als Schleife
Stellen Sie sich Rekursion jedenfalls als Schleife vor. Es sind zwei Kündigungsfälle definiert:
(<= number 0)
und(= number 1)
. In diesen Fällen gibt die Funktion eine einfache Zahl zurück.Im rekursiven Fall gibt die Funktion die Summe dieser Zahl und das Ergebnis der Funktion mit zurück
number - 1
. Eventuell wird die Funktion mit1
einer Zahl kleiner oder gleich Null aufgerufen .Das rekursive Fallergebnis lautet daher:
Nimm zum Beispiel
(triangle-using-cond 4)
. Lassen Sie uns den endgültigen Ausdruck akkumulieren:In der ersten Iteration
number
folgt4
also die(> number 1)
Verzweigung. Wir bauen einen Ausdruck auf(+ 4 ...
und rufen die Funktion mit(1- 4)
, dh auf(triangle-using-cond 3)
.jetzt
number
ist3
und das Ergebnis ist(+ 3 (triangle-using-cond 2))
. Der Gesamtergebnisausdruck ist(+ 4 (+ 3 (triangle-using-cond 2)))
.number
ist2
jetzt, so ist der Ausdruck(+ 4 (+ 3 (+ 2 (triangle-using-cond 1))))
number
ist1
jetzt, und wir nehmen den(= number 1)
Zweig, was zu einer langweiligen1
. Der ganze Ausdruck ist(+ 4 (+ 3 (+ 2 1)))
. Beurteilen Sie, dass von innen nach außen und Sie bekommen:(+ 4 (+ 3 3))
,(+ 4 6)
, oder einfach nur10
.quelle
message (...)
trifft,C-x C-e
zeigt das Endergebnis (10) nichts anderes? Vermisse ich etwas?Edebug
in die Tat umsetzt ?C-u C-M-x
mit einem Punkt in der Funktion, um das Problem zu beheben. Dann einfach die Funktion wie gewohnt ausführen.(message ...)
Zeug druckt in den*Message*
Puffer.Das Substitutionsmodell für die Prozeduranwendung von SICP kann den Algorithmus erklären, um Code wie diesen zu verstehen.
Ich habe auch Code geschrieben, um dies zu erleichtern.
lispy-flatten
von lispy package macht das. Hier ist das Ergebnis der Bewerbunglispy-flatten
bei(triangle-using-cond 4)
:Sie können den obigen Ausdruck folgendermaßen vereinfachen:
Dann noch einmal abflachen:
Das Endergebnis:
quelle
Dies ist nicht spezifisch für Emacs / Elisp, aber wenn Sie einen mathematischen Hintergrund haben, ist eine Rekursion wie eine mathematische Induktion . (Oder wenn Sie nicht: Wenn Sie Induktion lernen, ist es wie eine Rekursion!)
Beginnen wir mit der Definition:
Wann
number
ist4
, gilt keine der beiden ersten Bedingungen, so dass es nach der dritten Bedingung(triangle-using-cond 4)
ausgewertet wird : wird ausgewertet als(+ number (triangle-using-cond (1- number)))
, nämlich als(+ 4 (triangle-using-cond 3))
.Ebenso
(triangle-using-cond 3)
wird ausgewertet als(+ 3 (triangle-using-cond 2))
.Ebenso
(triangle-using-cond 2)
wird ausgewertet als(+ 2 (triangle-using-cond 1))
.Aber für
(triangle-using-cond 1)
gilt die zweite Bedingung, und es wird als ausgewertet1
.Ein Tipp für alle, die Rekursion lernen: Vermeiden Sie es
Wenn Sie sich selbst davon überzeugen möchten, ob
(triangle-using-cond 4)
die richtige Antwort zurückgegeben wird, gehen Sie einfach davon aus, dass(triangle-using-cond 3)
die richtige Antwort zurückgegeben wird, und überprüfen Sie, ob sie in diesem Fall korrekt ist. Natürlich müssen Sie auch den Basisfall überprüfen.quelle
Die Berechnungsschritte für Ihr Beispiel sehen folgendermaßen aus:
Die 0-Bedingung ist eigentlich nie erfüllt, da 1 als Eingabe die Rekursion bereits beendet.
quelle
(1)
ist kein gültiger Ausdruck.M-x calc
. :-) Im Ernst, ich wollte die Berechnung zeigen, nicht die Lisp-Bewertung.(4 +
statt(+ 4
in Ihrer Antwort ist ... :)Ich denke, es ist ziemlich einfach, dafür braucht man keinen Emacs Lisp, es ist nur Grundschulmathematik.
f (0) = 0
f (1) = 1
f (n) = f (n - 1) + n, wenn n> 1 ist
also ist f (5) = 5 + f (4) = 5 + 4 + f (3) = 5 + 4 + 3 + 2 + 1 + 0
Jetzt ist es offensichtlich.
quelle