2016-12-05 2 views
3

Ich stolperte auf this Artikel, der den Y Combinator erklärt. Der Code ist in Scheme, aber ich versuche, mit Common Lisp zu arbeiten.Definieren und Verwenden von Funktionen in Variablen in Common Lisp

Allerdings habe ich Probleme mit der Übersetzung von Scheme zu Common Lisp. Schema verwendet einen einzelnen Namespace für Funktionen und (andere) Variablen, aber Common Lisp verwendet unterschiedliche Namespaces für Funktionen und Variablen. Wie kann ich diesen Unterschied beheben, um den Common Lisp Code zu erhalten?

Scheme Code

Hier einig Scheme-Code aus dem Tutorial.

Am Anfang der Autor die Fakultäts-Funktion definiert:

(define (factorial n) 
    if (= n 0) 
    1 
    (* n (factorial (- n 1))))) 

und übersetzt sie in diese:

(define factorial 
    (lambda (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1)))))) 

Da (nach Ansicht des Autors) das ist, was Schema funktioniert:

Schema einfach übersetzt die erste Definition in die zweite vor der Auswertung. Also alle Funktionen in Scheme sind wirklich Lambda Ausdrücke.

Common Lisp

ich versucht, sowohl die oben Schnipsel in Common Lisp neu zu schreiben, um diesen Übergang von der ersten Form in die zweite zu imitieren. Aber es gibt keine in CL, noch hat es einen einzigen Namensraum. Also habe ich versucht mich zu betrügen.

Umschreiben der erste Schema Definition in Common Lisp war einfach:

(defun factorial (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1))))) 

aber (für mich) diese in die zweite Definition übersetzen ein bisschen schwieriger war. Ich übersetzte es wie folgt aus:

(setf (symbol-function 'factorial) 
    (lambda (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1)))))) 

Ist das eine schlechte Art und Weise, dies zu tun (oder gibt es eine bessere Art und Weise)? Es scheint zu funktionieren, aber der Compiler gibt mir eine Stilwarnung: undefined Funktion: Fakultät.

Antwort

3

In gewisser Weise ist es wie folgt aus:

(setf (symbol-function 'factorial) 
     (labels ((factorial (n) 
       (if (= n 0) 
        1 
        (* n (factorial (- n 1)))))) 
     #'factorial)) 

LABELS die lokale Funktion factorial definiert. Innerhalb der Definition der lokalen Funktion factorial sind alle Aufrufe an factorial zu dieser Funktion. Wir geben diese Funktion dann aus dem Beschriftungsausdruck zurück.Sie können also rekursive Funktionen definieren, bei denen der rekursive Aufruf nicht auf eine undefinierte Funktion erfolgt.

Wenn Sie Common Lisp-Implementierungen betrachten, können Sie sehen, dass DEFUN oft in nicht-portable Konstrukte wie benannte Lambda-Funktionen erweitert wird. Zusätzlich hat DEFUN auch Kompilierzeit-Nebenwirkungen.

2

Die Umwandlung ist in der gemeinsamen Liste nicht sinnvoll.

Die CL defun macht normalerweise viel mehr als nur (setf fdefinition).

Das können Sie sehen, indem Sie (macroexpand-1 '(defun foo (a b c) (bar c a b))) auswerten.

Die eigentliche Frage ist - warum versuchen Sie das zu tun?

+0

Ich lerne einfach besser den Code selbst eingeben anstatt nur zu lesen. Vielleicht ist dies in diesem Fall nicht die beste Idee wegen der Unterschiede zwischen Scheme und Common Lisp. – Frank

1

Wenn ich richtig verstehe, befasst sich die Hauptaufgabe Ihrer Frage mit der Übersetzung zwischen einer "Lisp-1" and a "Lisp-2".

Schema ist ein "Lisp-1" - es hat einen einzigen Namespace für Funktionen und Variablen. Common Lisp hingegen ist ein "Lisp-2" - es hat separate Namespaces für Funktionen und Variablen.

In Schema können Sie

(define foo (lambda (...) ...)) 

schreiben und rufen dann foo wie:

(foo ...) 

Wir foo auf genau die gleiche Art und Weise in Common Lisp und definieren können, aber wenn wir versuchen, Anruffoo mit dieser Syntax wird Ihr Programm abstürzen. Dies liegt daran, foo ist in der Variable Namespace, nicht die Funktion Namespace.

Wir können mit funcall dieses Problem umgehen foo aufzurufen:

(funcall foo ...) 

Das ist eine kurze Einführung. Die Common Lisp Cookbook Seite auf Functions bietet zusätzliche Details, die Sie nützlich finden könnten.

+0

Ja, der unterschiedliche Namespace-Ansatz ist das Problem. Ich muss entscheiden, ob ich die Funktion in der Wertzelle speichern soll (und sie mit 'funcall' aufrufen soll) oder benutze Rainers Lösung (die ich glaube, ich werde es versuchen). Der in der OP gezeigte Übergang ist nur der erste einer langen Reihe von Übergängen, die letztendlich ergeben werden. (Definieren Sie fast-faktoriell (Lambda (f) (Lambda (n) (wenn (= n 0) (* n (f (- n 1))))))) (definiere Y (Lambda (f) ((Lambda (x) (xx)) (Lambda (x) (f (xx)))))) (factorial (Y fast-factorial)) definieren ''. – Frank

+0

@Frank, rechts. Der Y-Kombinator beinhaltet das Übergeben einer Funktion als einen Parameter und das Anwenden dieser Funktion. Also ich denke du brauchst 'funcall' egal wie du da hinkommst. –