Question

Je lis sur Paradigmes de programmation d'intelligence artificielle de Peter Norvig, et je suis tombé sur une question que je ne peux pas résoudre moi-même (ce qui est mon introduction à Lisp). La question est tout à fait un petit, vraiment, mais évidemment pas un mon petit cerveau peut résoudre.

Pourquoi est-ce que lorsque la valeur d'une fonction est une lambda, il est une erreur d'utiliser cette fonction comme premier élément à une liste. Par exemple:

Lisp:

(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)

J'espère que le sens de marques!

Était-ce utile?

La solution

Ceci est une bonne question, et celle où Common Lisp peut être assez déroutant. La chose est que pour des raisons historiques, Common Lisp a deux espaces de noms - une pour les fonctions et une autre pour les valeurs. Pour ce faire, il y a deux règles d'évaluation différentes pour la position de la tête d'une application de fonction et pour le reste - la première évaluerons un symbole comme un nom de fonction, et le second évaluerons un symbole comme une référence variable. Il y a évidemment des cas où la valeur est en fait une fonction - par exemple, si vous écrivez une fonction mapcar, vous aurez envie de faire quelque chose comme

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

mais cela ne fonctionnera pas - il se plaindra de f étant une fonction inconnue. Pour ces cas, il existe une fonction spéciale appelée funcall, qui reçoit une fonction et argument de la fonction, et appliquera la fonction comme d'habitude - et depuis funcall est une fonction simple, ses arguments sont tous évalués comme d'habitude (comme des valeurs). Ainsi, le ci-dessus devrait être fixé en l'utilisant:

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

Comme vous pensez probablement maintenant, il y a les cas de miroir - où vous voulez évaluer quelque chose en fonction et pas comme valeur. Par exemple, cela ne fonctionne pas:

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

car il fait référence à la variable 1+ et non la fonction. Pour ces cas, il existe une forme spéciale appelée function qui évalue son contenu en fonction et retourne comme valeur:

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

et il peut être abrégé avec #':

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

Ce n'est pas la fin de cette histoire - pour donner quelques exemples:

  • dans certains cas, d'un simple nom cité peut fonctionner en fonction - par exemple '1+ dans les dernières œuvres par exemple - mais cela est une sorte de hack qui ne peut voir que des noms globalement reliés, de sorte #' est presque toujours mieux

  • un hack similaire peut être utilisé avec lambda - de sorte que vous pouvez utiliser (my-mapcar '(lambda (x) (1+ x)) '(1 2 3)), en fait, vous pouvez utiliser (list 'lambda '(x) '(1+ x)) qui est encore pire (et IIRC, non portable), mais en utilisant des œuvres de (lambda (x) (1+ x)) car il est enveloppé implicitement dans un #' (essayez l'expansion d'une forme de lambda comme une macro et vous verrez). Un hack lié en fait bien d'utiliser une expression de lambda comme la tête d'une application de fonction (ce qui est l'une des choses que vous avez essayé).

  • let etc lier les valeurs locales, et dans certains cas, vous aurez envie de lier les fonctions locales au lieu - pour cela, il y a des nouvelles constructions de liaison: flet et labels

Si tout cela semble bizarre et / ou trop compliqué, alors vous n'êtes pas seul. Il est l'une des principales différences entre Common Lisp et Scheme. (La différence conduit alors à des changements dans les idiomes communs dans les deux langues: le code Scheme a tendance à utiliser des fonctions d'ordre supérieur beaucoup plus fréquemment que le code Common Lisp Comme d'habitude avec ce genre de questions religieuses, certaines personnes plaident en faveur de ce que fait CL, prétendant. que les fonctions d'ordre supérieur sont confus, alors ils comme le rappel explicite dans le code.)

Autres conseils

((lambda (x) ...) ...) est un cas particulier dans les règles hardcoded évaluateur. Il a pas d'évaluer le premier élément en forme et en utilisant le résultat comme un cas général. Il est normal que vous devez utiliser funcall ou apply pour appeler une fonction qui est le résultat de l'évaluation d'une autre forme.

La raison commune Lisp ne permet pas une fonction qui renvoie un lambda pour être le premier élément dans une expression doit faire la distinction entre Lisp-1 et Lisp-2 .

Si Common Lisp a permis ((some-func) 1) être équivalent à (funcall (some-func) 1), alors il pourrait être perçu comme incohérent de ne pas laisser dire aussi (let ((f #'some-func)) (f 1)) Au lieu de demander (funcall f 1).

scroll top