Frage

Ich lese über Peter Norvigs Paradigmen der Programmierung künstlicher Intelligenz und ich bin auf ein Problem gestoßen, das ich nicht alleine lösen kann (dies ist meine Einführung in LISP). Das Problem ist wirklich eine ziemlich kleine, aber offensichtlich nicht eines, das mein kleines Gehirn lösen kann.

Warum ist es ein Fehler, diese Funktion als erstes Element für eine Liste zu verwenden, wenn der Wert einer Funktion ein Lambda ist. Zum Beispiel:

Lispeln:

(defun some-func ()
  #'(lambda (x) x))

;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)

Ich hoffe das ergibt Sinn!

War es hilfreich?

Lösung

Dies ist eine gute Frage, und eine, bei der Common Lisps ziemlich verwirrend sein kann. Die Sache ist, dass Common Lisp aus historischen Gründen zwei Namespaces hat - einen für Funktionen und eine für Werte. Um dies zu erreichen, gibt es zwei verschiedene Bewertungsregeln für die Kopfposition einer Funktionsanwendung und für den Rest - das erste wird ein Symbol als Funktionsname bewertet, und das zweite bewertet ein Symbol als variable Referenz. Es gibt offensichtlich Fälle, in denen der Wert tatsächlich eine Funktion ist - zum Beispiel, wenn Sie a schreiben mapcar Funktion, Sie möchten so etwas tun

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (f (car l)) (my-mapcar f (cdr l)))))

Aber das wird nicht funktionieren - es wird sich darüber beschweren f eine unbekannte Funktion sein. In diesen Fällen gibt es eine spezielle Funktion genannt funcall, was eine Funktion und ein Argument für die Funktion empfängt und die Funktion wie gewohnt anwendet - und seitdem funcall ist eine einfache Funktion, ihre Argumente werden alle wie gewohnt bewertet (als Werte). Das obige sollte also durch die Verwendung festgelegt werden:

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (funcall f (car l)) (my-mapcar f (cdr l)))))

Wie Sie wahrscheinlich jetzt verdächtigen, gibt es die Mirror -Fälle - wo Sie etwas als Funktion und bewerten möchten und nicht als Wert. Zum Beispiel funktioniert dies nicht:

(my-mapcar 1+ '(1 2 3))

Weil es sich auf die bezieht 1+ variabel und nicht die Funktion. In diesen Fällen gibt es eine spezielle Form namens function Dies bewertet seinen Inhalt als Funktion und gibt es als Wert zurück:

(my-mapcar (function 1+) '(1 2 3))

und es kann mit abgekürzt werden #':

(my-mapcar #'1+ '(1 2 3))

Das ist nicht das Ende dieser Geschichte - um ein paar Beispiele zu geben:

  • In einigen Fällen kann ein einfacher zitierter Name als Funktion funktionieren - zum Beispiel '1+ Im letzten Beispiel funktioniert - aber dies ist eine Art Hack, der nur global gebundene Namen sehen kann, also #' ist fast immer besser

  • Ein ähnlicher Hack kann mit verwendet werden lambda - damit Sie verwenden können (my-mapcar '(lambda (x) (1+ x)) '(1 2 3)), In der Tat könnten Sie verwenden (list 'lambda '(x) '(1+ x)) Was noch schlimmer ist (und IIRC, nicht portierbar), aber verwenden (lambda (x) (1+ x)) funktioniert, da es implizit in a eingewickelt ist #' (Versuchen Sie, a zu erweitern lambda Form als Makro und Sie werden es sehen). Ein verwandter Hack macht es in Ordnung, a zu verwenden lambda Ausdruck als Kopf einer Funktionsanwendung (eine der Dinge, die Sie ausprobiert haben).

  • let usw. binden lokale Werte, und in einigen Fällen möchten Sie stattdessen lokale Funktionen binden - dafür gibt es neue Bindungskonstrukte: flet und labels

Wenn all dies seltsam und/oder übermäßig kompliziert aussieht, sind Sie nicht allein. Es ist einer der Hauptunterschiede zwischen gemeinsamem Lisp und Schema. (Der Unterschied führt dann zu Änderungen der gemeinsamen Redewendungen in beiden Sprachen: Der Schemecode verwendet tendenziell viel häufiger als gemeinsamer LISP -Code. Wie bei solchen religiösen Fragen üblich, argumentieren einige Menschen für das, was CL tut, und behaupten Diese Funktionen höherer Ordnung sind verwirrend, so dass sie die explizite In-Code-Erinnerung mögen.)

Andere Tipps

((lambda (x) ...) ...) ist ein festcodierter Sonderfall in den Bewerterregeln. Es wird nicht das erste Element in der Form bewertet und das Ergebnis als allgemeiner Fall verwendet. Es ist normal, dass Sie verwenden müssen funcall oder apply Aufrufen einer Funktion, die das Ergebnis der Bewertung eines anderen Formulars ist.

Der Grund, warum Common Lisp nicht zulässt, dass eine Funktion, die ein Lambda zurückgibt Lisp-1 und Lisp-2.

Wenn das gemeinsame Lisp erlaubt ist ((some-func) 1) gleichwertig sein mit (funcall (some-func) 1), und dann könnte es als inkonsistent empfunden werden, nicht auch sagen zu lassen (let ((f #'some-func)) (f 1)) eher als zu verlangen (funcall f 1).

Es gibt tatsächlich eine sehr gute Rechtfertigung dafür, solche Formen nicht zu unterstützen: Sie sind mehrdeutig. Betrachten Sie den Weg Funktionsnamen sind in gemeinsamer Lisp: definiert:

Funktionsname n. 1. (in einem Umgebung) EIN Symbol oder ein aufführen (setf symbol) das ist das Name von a Funktion darin Umgebung. 2. a Symbol oder ein aufführen (setf symbol).

Wie sollte sich eine Form wie das folgende verhalten?

((setf car) 10 x)

Sollte dieses Set xAuto zum Wert 10, oder sollte es das Formular ausführen (setf car) (Dies wäre ein Fehler) und versuchen, seinen Rückgabewert als eine Funktion zum Aufrufen der Argumente zu verwenden 10 und x? Common Lisp gibt kein Verhalten an, und es ist mir überhaupt nicht klar, dass das eine schlechte Wahl ist. Soweit ich sehen kann, verhindert nichts im Standard, dass die Definition der Definition gültiger Formulare eine größere Auswahl an Bedienungsnamen erweitert (so dass SetF-Funktionen mit besonderer Casing hier auch hier nicht wirklich helfen würden).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top