Yliur <
yl...@free.fr> writes:
> Bonjour
>
> Pour passer une fonction en paramètre à une autre fonction (par exemple
> à mapcar), il faut passer un "indicateur de fonction". Par exemple une
Traduisons plutôt "function designator" par « désignateur de fonction ».
> fonction elle-même ou un symbole référençant la fonction.
>
> Le plus simple me semble d'écrire
> (mapcar 'fonction
> liste)
> ou encore
> (mapcar (lambda ...)
> liste)
>
> Pourtant on trouve beaucoup de code sous cette forme dans les exemples
> de l'Hyperspec :
> (mapcar #'fonction
> liste)
> ou
> (mapcar #'(lambda ...)
> liste)
>
> Est-ce qu'il y a une différence ?
Il y a des différences.
Si X est un symbole dans le paquetage COMMON-LISP, alors:
'X et #'X désignent toujours la même fonction (voir 11.1.2.1.2)
sinon:
'X et #'X peuvent désigner des fonctions différentes, dans un
contexte lexical où une fonction local nommée X est définie.
(defun f () 'global)
(flet ((f () 'local))
(list (funcall (quote sin) 42) (funcall (function sin) 42)
(funcall (quote f)) (funcall (function f))))
--> (-0.9165215 -0.9165215 GLOBAL LOCAL)
Si X est une forme lambda, alors 'X retourne une liste, et n'est pas un
désignateur de fonction:
(listp '(lambda () 'hi)) --> T
Note que l'on peut obtenir une fonction à partir d'une forme lambda de
trois manières:
(let ((lambda-form (lambda () (quote hello))))
(list
(eval lambda-form)
(compile nil lambda-form)
(coerce lambda-form (quote function))))
mais en général, EVAL va retourner une fonction non-compilée, COMPILE va
retourner une fonction compiler (et prendre du temps pour le faire),
tandis que COERCE va retourner l'une ou l'autre. Tout dépend de
l'implémentation:
$ clall -r '(let ((lambda-form (lambda () (quote hello))))
(list
(eval lambda-form)
(compile nil lambda-form)
(coerce lambda-form (quote function))))'
Clozure Common Lisp --> (#<Anonymous Function #x30200064F16F>
#<Anonymous Function #x30200064F16F>
#<Anonymous Function #x30200064F16F>)
CLISP --> (#<FUNCTION :LAMBDA NIL 'HELLO>
#<COMPILED-FUNCTION NIL>
#<FUNCTION :LAMBDA NIL 'HELLO>)
CMU Common Lisp --> (#<Interpreted Function (LAMBDA # 'HELLO) {581B8231}>
#<Function "LAMBDA NIL" {581C9211}>
#<Interpreted Function (LAMBDA # 'HELLO) {581B8231}>)
SBCL --> (#<FUNCTION (LAMBDA #) {1002EF09B9}>
#<FUNCTION (LAMBDA #) {1002EF09B9}>
#<FUNCTION (LAMBDA #) {1002EF09B9}>)
========================================================================
Il peut sembler plus simple de passer un symbole désignant une fonction
à une fonction d'ordre supérieur comme mapcar, mais la sémantique n'est
pas forcément la même, même sans fonction locale!
(defun f (x)
(prog1 (list x 'initial)
(setf (symbol-function 'f)
(lambda (z) (declare (ignore z)) (list x 'surprise)))))
(mapcar (quote f) '(1 2 3))
--> ((1 INITIAL) (1 SURPRISE) (1 SURPRISE))
(defun g (x) ; même définition que f.
(prog1 (list x 'initial)
(setf (symbol-function 'g)
(lambda (z) (declare (ignore z)) (list x 'surprise)))))
(mapcar (function g) '(1 2 3))
--> ((1 INITIAL) (2 INITIAL) (3 INITIAL))
(g 42)
--> (3 SURPRISE)
C'est aussi à prendre en compte lorsqu'on travaille avec de multiples
fils d'exécution (threads).
Par exemple si on passe (quote f) à une boucle d'évènement, cette boucle
prendra en compte une nouvelle définition de la fonction F dès que celle
ci est définie dans un autre fil. Si on avait passé (function f), alors
la même fonction sera toujours utilisee dans la boucle d'évènement, même
si la fdefinition de f change. Les deux sémantiques sont utiles.
Personnellement, dans mes programmes, j'écris (function f) sauf si un
symbole désignant la fonction est nécessaire; mais interactivement dans le
REPL je tape 'f.
> Le # indique qu'il s'agit d'une indication au lecteur, si j'ai bien compris.
Pas vraiment. #\# est un caractère comme un autre. Il est traité par le
même algorithme de lecture de lisp que n'importe quel autre caractère.
Voir cet algorithme:
http://www.lispworks.com/documentation/HyperSpec/Body/02_b.htm
En pratique, #\# est une "reader macro", qui aiguille (dispatch) selon
le caractère qui suit (il peut un avoir des arguments et des drapeaux avant
le caractère en question). #\# et le caractère qui suit forment une
"dispatching reader macro".
La "dispatching reader macro" « #' » lit l'expression qui suit et
l'enferme dans une forme CL:FUNCTION:
#'x est lu comme (CL:FUNCTION x)
de la même manière que la "reader macro" « ' » lit l'expression qui suit
et l'enferme dans une forme CL:QUOTE:
'x est lu comme (CL:QUOTE x)
D'un autre côté, la "reader macro" « " » lit les caractères qui suivent
jusqu'à un autre " qui ne soit pas gardé par un \, et les accumule dans
une chaîne qui est retournée comme lue:
"x" est lu comme "x"
etc.
Note comment la routine de lecture de lisp ne sait vraiment lire que les
nombres entiers, rationels et flotants, et les symboles. Tout le reste
de la syntaxe lisp est implémentée par des "reader macros" et
"dispatching reader macros" qui sont remplaçable à volonté par le
programmeur.
> Est-ce qu'il fait le lien avec la fonction directement au moment de la
> lecture, si le symbole contient déjà une fonction, et que ça ne change
> plus ensuite si on change la fonction associée au symbole ?
???
> Pour le cas avec lambda je ne comprends pas l'intérêt. A moins que ce
> soit juste pour repérer immédiatement l'indication d'une fonction ?
En CLtL2, cl:lambda n'était pas une macro, donc il fallait écrire
(function (lambda ...))
pour obtenir une fonction anonyme. Je suppose que certains programmeurs
ont en effet peut que leur code puisse rencontrer une implémentation
CLtL2, alors que toutes les implémentations courrantes sont conforme à
la norme CL.
Ou peut être comme tu dis, ils le font pour homogénéïser la notation:
(funcall (function f))
(funcall (function (lambda () ...)))
Personnellement je préfère laisser travailler le compilateur, et j'écris:
(funcall (function f))
(funcall (lambda () ...))
--
__Pascal Bourguignon__
http://www.informatimago.com/
A bad day in () is better than a good day in {}.