2016-04-13 10 views
1

Ich habe gestern mit dem Lispeln angefangen, also bitte entschuldigen Sie, wenn ich wirklich einen Anfängerfehler mache. Ich versuche, eine Funktion zu erstellen, die die Glockenzahl mit dem Glockendreieck berechnet und meine rekursive Dreiecksfunktion nicht richtig funktioniert. Ich bin mir auch sicher, wenn ich meine rekursive Dreiecksfunktion funktionierte, dass meine rekursive Glockenfunktion irgendwie auch gebrochen ist.Lisp rekursive Funktion fehlt Basisfall beim ersten Aufruf

Wenn ich meine Dreieck Funktion zu testen habe ich den Ausgang:

(defun bell(l n) 
    (if(< n 1)(list 1)) 
    (if (= n 1)(last l)) 
    (bell (triangle (reverse l) (last l) (list-length l)) (- n 1)) 
) 
(defun triangle(pL nL i) 
    (if(<= i 0) 
     (write "equals zero!") 
     (reverse nL) 
    ) 
    (triangle pL (append (list (+ (nth i pL) (nth i nL))) nL) (- i 1)) 
) 
(write (triangle '(1) '(1) 0)) 


=> 


"equals zero!""equals zero!" 
*** - NTH: -1 is not a non-negative integer 

Aus irgendeinem Grund, es ist mein Debug-Code gedruckt wird zweimal, obwohl die Funktion sollte mein Basismodell, von dem ersten Aufruf treffen.

+0

Ich rollte die Bearbeitung auf den Titel, weil während OP zurück ** Wenn ** versucht wird, ein Dreieck zu erstellen, hat OP auch das eigentliche Problem hier identifiziert: Die Funktion * endet nicht mit dem erwarteten Basisfall, und das liegt an der Gruppierung der ** if ** s. Während eine Lösung für das Dreieck OP helfen könnte, konzentriert sich die Frage wirklich darauf, warum zum Beispiel '(defun foo (n) (if (= n 0) 35) 42) immer 42 zurückgibt, selbst wenn n ist 0. Das liegt daran, dass der Code das Ergebnis von ** if ** ignoriert. –

+0

@JoshuaTaylor Ich verstehe deinen Standpunkt. Der OP fragt nicht, wie er das Dreieck lösen soll, sondern warum er diesen Fehler hat. Wie auch immer, ich dachte, der Beitrag könnte hilfreicher sein und mit einem passenderen Titel leicht gefunden werden, da der aktuelle nicht einmal den Fehler darstellt. – FrankS101

Antwort

3

Aus irgendeinem Grund , es druckt meinen Debug-Code zweimal, obwohl die Funktion meinen Basisfall beim ersten Aufruf erfüllen sollte.

Es wird zweimal gedruckt, weil if nicht tut, was Sie denken, dass es tut. Der erste if-Test ist wahr, daher ist gleich null! wird gedruckt.Danach wird ein rekursiver Aufruf der Dreiecksfunktion aufgerufen. Der Test ist wieder wahr (-1 < = 0), also ist gleich Null! wird erneut gedruckt. Schließlich erhalten Sie einen Fehler, weil nthcdr Funktion mit -1 aufgerufen wird. Ich empfehle Ihnen dringend einen guten Lisp-Debugger. Der von Lispworks ist ziemlich gut.

Ich verstehe ehrlich gesagt nicht die Logik dessen, was Sie mit Ihrem Code erreichen wollten. so schrieb ich meine:

(defun generate-level (l &optional (result)) 
    "given a list l that represents a triangle level, it generates the next level" 
    (if (null l) result 
    (if (null result) 
     (generate-level l (list (car (last l)))) 
     (generate-level (cdr l) (append result 
             (list (+ (car l) 
               (car (last result))))))))) 


(defun bell (levels &optional (l)) 
    "generate a bell triangle with the number of labels given by the first parameter" 
    (unless (zerop levels) 
     (let ((to-print (if (null l) (list 1) (generate-level l)))) 
      (print to-print) 
      (bell (1- levels) to-print)))) 

Dinge zu verstehen, die Umsetzung:

  1. &optional (parameter): Dieser Parameter ist optional und Null standardmäßig.
  2. append verkettet zwei Listen. Ich verwende es, um in die Rückseite der Liste einzufügen.
  3. let ((to-print x)) erstellt eine neue variable Bindung (lokale Variable) namens to-print und initialisiert auf x.
  4. Ich habe vergessen zu erwähnen, wie fast if Werke in Common Lisp: (if (= x 1) y z) Mittel, wenn x dann gleich 1 ist y zurückkehren, sonst z.

Nun, wenn Sie die Funktion aufrufen, ein Bell-Dreieck von 7 Ebenen zu schaffen:

CL-USER 9 > (bell 7) 

(1) 
(1 2) 
(2 3 5) 
(5 7 10 15) 
(15 20 27 37 52) 
(52 67 87 114 151 203) 
(203 255 322 409 523 674 877) 
NIL 

Es wäre schöner, es mit der sachgemäßer Polsterung zu drucken, wie folgt aus:

    1 
       1  2 
       2  3  5 
      5  7 10 15 
     15 20 27 37 52 
    52 67 87 114 151 203 
203 255 322 409 523 674 877 

aber ich überließ das dem Leser als Übung.

+1

Ein paar kleine Kommentare zum Code: Sie scheinen ein unnötiges 'AND' in' BELL' zu haben. Sie sollten auch "UNLESS" anstelle von "(WHEN (NOT ...))" verwenden, und das '(IF (NULL L) ...)' könnte innerhalb der 'LET'-Bindung verschoben werden, so dass Sie es nicht tun muss Code wiederholen. Verwenden Sie auch 'COND' anstelle von verschachtelten' IF's in 'GENERATE-LEVEL'. – jkiiski

+0

@jkiiski Danke! Ich habe seit einiger Zeit nicht in Lisp programmiert und ich bin rostig. – FrankS101

+0

@ FrankS101 Vielen Dank für die ausführliche Erklärung und die Lösung. Ich habe schließlich herausgefunden, wie die if-Bedingung funktioniert. Es war einer meiner ersten Sprünge von der Imperativ- zur funktionalen Programmierung, so dass ich ein bisschen herumlungerte. Ich brauchte nur die n-te Glockennummer anstelle des gesamten Dreiecks, und dies war der am wenigsten komplizierte Weg, um das zu erreichen, also wählte ich diesen Ansatz. –

2

Ihre wenn s haben keine Wirkung. Sie werden ausgewertet und produzieren Ergebnisse, aber dann verwerfen Sie sie. Genau wie

(defun abc() 
    'a 
    'b 
    'c) 

würde bewerten 'a und ' b die Symbole a und b, zu erzeugen, und würde dann auszuwerten ‚c das Symbol zu erzeugen c, die würden dann zurückgegeben werden. Im Falle von

(if(<= i 0) 
     (write "equals zero!") ; then 
     (reverse nL)   ; else 
    ) 

Sie vergleichen, ob i kleiner oder gleich Null ist, und wenn es ist, drucken Sie gleich Null ist, und wenn es nicht ist, Sie (nicht-destruktiv) Reverse nL und verwerfe das Ergebnis. Dann beenden Sie die Funktion durch einen Anruf an Dreieck. Es scheint, als ob Sie wahrscheinlich zurückgeben wollen die umgekehrte nL, wenn ich kleiner oder gleich Null ist. Verwenden Sie cond statt, da Sie mehrere Körperformen haben kann, wie in:

(cond 
    ((<= i 0) (write ...) (reverse nL)) 
    (t (triangle ...))) 

Sie auch wenn mit progn zur Gruppe der Formulare verwenden könnte:

(if (<= i 0) 
    (progn 
    (write ...) 
    (reverse nL)) 
    (triangle ...)) 

Ihre andere Funktion hat das gleiche Problem. Wenn Sie in diesen ersten Fällen Werte zurückgeben möchten, müssen Sie ein Formular verwenden, das sie tatsächlich zurückgibt. Zum Beispiel:

(if (< n 1) 
    (list 1) 
    (if (= n 1) 
     (last l) 
     (bell #| ... |#))) 

Mehr idiomatische würde cond und mit Liste statt l sein, die viel wie aussieht:

(cond 
    ((< n 1) (list 1)) 
    ((= n 1) (last list)) 
    (t (bell #| ... |#))) 
-1

Vielen Dank für die Erklärungen. Ich kam schließlich zu dem Code unten. Ich erkannte, dass das, wenn Block etwas gearbeitet wie ..

(if (Bedingung) (Ausführungsanweisung) (sonst diese Anweisung ausführen))

(defun bell(l n) 
    (if (< n 2)(last l) 
     (bell (triangle l (last l) 0) (- n 1)) 
    ) 
) 
(defun triangle(pL nL i) 
    (if(= i (list-length pL)) nL 
     (triangle pL (append nL (list (+ (nth i pL) (nth i nL)))) (+ i 1)) 
    ) 
) 
(write (bell (list 1) 10))