Utilisation d'une valeur lambda de fonction en tant que premier élément de la liste
-
27-10-2019 - |
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!
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 delambda
comme une macro et vous verrez). Un hack lié en fait bien d'utiliser une expression delambda
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
etlabels
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)
.
Il est en fait une très bonne justification pour ne pas soutenir de telles formes: Ils sont ambigus. Pensez à la façon dont les sont définis en Common Lisp:
nom de la fonction n . 1. (dans un environnement ) symbole ou
(setf symbol)
qui est le nom de fonction que environnement . 2. symbole ou(setf symbol)
.
Compte tenu de cela, comment devrait une forme comme le comportement à suivre?
((setf car) 10 x)
Si cela ensemble voiture de x
à la valeur 10
, ou doit-il exécuter la forme (setf car)
(ce qui serait une erreur) et essayez d'utiliser sa valeur de retour en fonction d'appeler les arguments 10
et x
? Common Lisp ne précise ni le comportement, et ce n'est pas du tout clair pour moi que c'est un mauvais choix. Après tout, pour autant que je peux voir, rien dans la norme empêche les implémentations conformes d'étendre la définition des formes valides pour soutenir une plus large gamme de noms d'opérateurs (si SETF-fonctions spéciales de boîtier n'aiderait pas vraiment ici, que ce soit).