2016-09-12 3 views
2

Hier ist eine Funktion, die ich schrieb, die eine Zahlliste basierend auf einem Startwert, Endwert und einer nächsten Funktion generieren wird.Optionales Funktionsargument mit Standardwert in Common Lisp

(defun gen-nlist (start end &optional (next #'(lambda (x) (+ x 1)))) 
    (labels ((gen (val lst) 
     (if (> val end) 
      lst 
      (cons val (gen (next val) lst))))) 
    (gen start '()))) 

jedoch, wenn es in die SBCL repl Eingabe ich die folgenden Warnungen erhalten:

; in: DEFUN GEN-NLIST 
;  (SB-INT:NAMED-LAMBDA GEN-NLIST 
;   (START END &OPTIONAL (NEXT #'(LAMBDA (X) (+ X 1)))) 
;  (BLOCK GEN-NLIST 
;   (LABELS ((GEN # 
;     #)) 
;   (GEN START 'NIL)))) 
; 
; caught STYLE-WARNING: 
; The variable NEXT is defined but never used. 

;  (NEXT VAL) 
; 
; caught STYLE-WARNING: 
; undefined function: NEXT 
; 
; compilation unit finished 
; Undefined function: 
;  NEXT 
; caught 2 STYLE-WARNING conditions 

Irgendwie hat es die Variable siehe nächste definiert ist, aber nicht verwendet. Und wo ich es verwende, wie in (next val), ist es eine undefinierte Funktion ?!

Klar mache ich hier etwas falsch. Ich kann einfach nicht herausfinden, was oder wie. Ist das die korrekte Art, optionale Funktionsargumente mit Standardwerten anzugeben?

+0

Wenn Sie eine/die Lösung bekannt geben möchten, sollten Sie es schreiben * als Antwort *. Nicht als Bearbeitung in die Frage aufnehmen. Fragen sind für Fragen. Antworten sind für Antworten. –

+0

Sinn macht. Geändert. –

Antwort

4

Sie haben das optionale Argument korrekt definiert, aber da Common Lisp ein Lisp-2 ist, wird zwischen Funktionen und Variablenwerten unterschieden. Die optionale Funktion ist als Variable verfügbar und muss über funcall aufgerufen werden.

ersetzen

(cons val (gen (next val) lst)) 

mit

(cons val (gen (funcall next val) lst)) 

und sowohl der Warnung über die nicht verwendeten Variablen und die Warnung über die nicht definierte Funktion verschwinden.

BTW: Ihre Standard-Funktion kann mit #'1+

+0

Auch sollte es sein '(gen (funcall ...) (Nachteile ...))' für Schwanz Rekursion zu arbeiten. – jkiiski

+0

@jkiiski Nein, das ändert die Reihenfolge der Dinge. Das ist eine ganz andere Funktion und müsste so geschrieben werden. Obwohl ja, der, den ich schrieb, ist nicht tail rekursiv. –

+1

@ λ- Sie müssen natürlich auch die Liste umkehren. Wie es jetzt ist, ist der 'LST' Parameter unnötig. – jkiiski

0

Im Fall ersetzt werden, wenn jemand jemals die Funktion Code in vollem Umfang benötigt, um zu sehen, dank die Antwort oben und die Kommentare, hier ist was ich am Ende mit:

(defun gen-nlist (start end &optional (next #'1+) (endp #'>)) 
    (labels ((gen (val) 
     (if (funcall endp val end) 
      '() 
      (cons val (gen (funcall next val)))))) 
     (gen start))) 

Und hier ist die rekursive Schwanz-Version, die von einem Funktionsaufruf Stapelüberlauf nicht leidet, wenn große Listen zu erstellen versuchen:

(defun gen-nlist (start end &optional (next #'1+) (endp #'>)) 
    (labels ((gen (val lst) 
     (if (funcall endp val end) 
      (reverse lst) 
      (gen (funcall next val) (cons val lst))))) 
     (gen start '()))) 

Verwendungsbeispiele:

>(gen-nlist 0 10)

(0 1 2 3 4 5 6 7 8 9 10)

>(gen-nlist 0 10 #'1+ #'equal)

(0 1 2 3 4 5 6 7 8 9)

>(gen-nlist 0 -10 #'1- #'<)

(0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)

>(gen-nlist -10 0)

(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0)

+1

Wenn Sie die automatische Einzugunterstützung durch einen Editor verwenden würden, würde der Code etwas anders eingerückt. Der letzte Anruf befindet sich in den LABELS. Nicht auf dem gleichen Niveau. Der Rumpf der GEN-Funktion muss mehr nach rechts eingerückt werden ... –

+1

Das dachte ich mir auch, aber mein Redakteur besteht aus irgendeinem Grund darauf, sich auf diese falsche Weise einzurücken. Ich überprüfe die automatischen Einzugsbindungen. –

7

Die einfache rekursive Version hat ein Hauptproblem: Stapelüberlauf für lange Listen. Es ist nützlich als Lernübung, aber nicht für Produktionscode.

Die typische effiziente Schleifeniterationslatenzzeit würde wie folgt aussehen:

(defun gen-nlist (start end &optional (next #'1+) (endp #'>)) 
    (loop for i = start then (funcall next i) 
     until (funcall endp i end) 
     collect i)) 
+0

Ich habe die obige loop-Version (L) und eine einfache rekursive Version (SR) und eine rekursive tail-Version (TR) (die letzten beiden gemäß meiner Antwort) und jeweils für verschiedene Bereiche getaktet. SR schlägt bei etwa 100.000 Elementen fehl, da der Aufrufstapel nicht mehr ausreicht. Aber L und TR geben beide nur bei 100.000.000 aus, weil sie keinen Heap mehr haben. L scheint ungefähr 30% oder so schneller zu sein. Warum ist das? Sowohl L als auch TR haben keinen Heap mehr, also müssen beide einen iterativen Prozess durchlaufen, richtig? Was ist dann anders an der Schleife, dass es schneller ist als die Tail-Rekursion? –