Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Petite question de forme (?) pour le passage de fonctions

10 views
Skip to first unread message

Yliur

unread,
Jan 10, 2012, 6:11:03 PM1/10/12
to

Bonjour

Pour passer une fonction en paramètre à une autre fonction (par exemple
à mapcar), il faut passer un "indicateur de fonction". Par exemple une
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 ? Le # indique qu'il s'agit d'une
indication au lecteur, si j'ai bien compris. 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 ?

Merci

Yliur

Pascal J. Bourguignon

unread,
Jan 11, 2012, 11:02:46 AM1/11/12
to
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 {}.

Yliur

unread,
Jan 11, 2012, 7:20:08 PM1/11/12
to
Le Wed, 11 Jan 2012 17:02:46 +0100
"Pascal J. Bourguignon" <p...@informatimago.com> a �crit :

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

Parce que si je modifie ce que r�f�rence l'un de ces symboles le
programme devient non conforme et son comportement non pr�visible, donc
c'est mal, c'est �a ? �a veut dire que je ne peux pas "�tendre" une
fonction de ce paquetage parce que le comportement qui en r�sulte n'est
pas fiable (par exemple si je veux red�finir '+ pour qu'il g�re de
nouveaux types, en disant : si c'est un de mes types tu fais �a, sinon
tu appelles l'ancienne fonction associ�e au symbole '+) ?

> [...]
(hop, je ne recopie pas tout, merci pour les explications)

D'accord, d�j� je comprends un peu mieux ce que fait #'f, qui veut
juste dire (function f). Je pensais que la fonction �tait choisie au
moment de la lecture, ou quelque chose comme �a. Non, en fait �a veut
juste dire que la fonction sera choisie au moment de l'ex�cution de
cette expression, alors qu'avec un symbole la fonction � ex�cuter (par
mapcar par exemple) sera choisie juste au moment de l'ex�cuter (ou de
mani�re g�n�rale, il est possible que la fonction soit choisie �
n'importe quel moment plus tard, en regardant dans le symbole).

Je pensais que les red�finitions de fonctions (avec flet par exemple)
masquaient dynamiquement la fonction associ�e au symbole concern�,
alors que finalement c'est un lien lexical, qui ne vaut que dans le
bloc et pas dans les fonctions appel�es par exemple. Je ne red�finis
pas de fonctions temporairement pour l'instant, mais par curiosit� :
comment red�finir le lien dynamique entre un symbole et une fonction ?
Est-ce possible ? J'ai tent� de modifier avec setf symbol-function ou
fdefinition dans un bloc let, mais �a m'a remplac� globalement la
d�finition de la fonction.

Pascal J. Bourguignon

unread,
Jan 12, 2012, 9:16:01 AM1/12/12
to
Yliur <yl...@free.fr> writes:

> Le Wed, 11 Jan 2012 17:02:46 +0100
> "Pascal J. Bourguignon" <p...@informatimago.com> a écrit :
>
>> 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)
>
> Parce que si je modifie ce que référence l'un de ces symboles le
> programme devient non conforme et son comportement non prévisible, donc
> c'est mal, c'est ça ? Ça veut dire que je ne peux pas "étendre" une
> fonction de ce paquetage parce que le comportement qui en résulte n'est
> pas fiable (par exemple si je veux redéfinir '+ pour qu'il gère de
> nouveaux types, en disant : si c'est un de mes types tu fais ça, sinon
> tu appelles l'ancienne fonction associée au symbole '+) ?

Oui.

Essayer de modifier le fbinding d'un symbole dans CL peut ne pas
avoir d'effet, ou peut provoquer une erreur.


>> [...]
> (hop, je ne recopie pas tout, merci pour les explications)
>
> D'accord, déjà je comprends un peu mieux ce que fait #'f, qui veut
> juste dire (function f). Je pensais que la fonction était choisie au
> moment de la lecture, ou quelque chose comme ça. Non, en fait ça veut
> juste dire que la fonction sera choisie au moment de l'exécution de
> cette expression, alors qu'avec un symbole la fonction à exécuter (par
> mapcar par exemple) sera choisie juste au moment de l'exécuter (ou de
> manière générale, il est possible que la fonction soit choisie à
> n'importe quel moment plus tard, en regardant dans le symbole).
>
> Je pensais que les redéfinitions de fonctions (avec flet par exemple)
> masquaient dynamiquement la fonction associée au symbole concerné,
> alors que finalement c'est un lien lexical, qui ne vaut que dans le
> bloc et pas dans les fonctions appelées par exemple.

Correct.


> Je ne redéfinis pas de fonctions temporairement pour l'instant, mais
> par curiosité : comment redéfinir le lien dynamique entre un symbole
> et une fonction ? Est-ce possible ? J'ai tenté de modifier avec setf
> symbol-function ou fdefinition dans un bloc let, mais ça m'a remplacé
> globalement la définition de la fonction.

Il n'y a pas en CL d'opérateur pour effectuer un lien dynamic sur le
slot fonction des symboles, seulement pour le slot valeur.


En clisp, on peut utiliser ext:letf http://clisp.org/impnotes.html#letf

[pjb@kuiper :0 ~]$ clisp -norc -ansi -q
[1]> (defun f ()
'(dynamic global))
F
[2]> (defun g ()
(funcall 'f))
G
[4]> (ext:letf (( (symbol-function 'f) (lambda () '(dynamic local)) ))
(list (funcall (function f))
(funcall (quote f))
(g)))
((DYNAMIC LOCAL) (DYNAMIC LOCAL) (DYNAMIC LOCAL))
[5]> (macroexpand '(ext:letf (( (symbol-function 'f) (lambda () '(dynamic local)) ))
(list (funcall (function f))
(funcall (quote f))
(g))))
(LET*
((#:TEMP-3076 'F) (#:NEW-3075 (SYMBOL-FUNCTION #:TEMP-3076))
(#:LETF-VALUE-3077 (LAMBDA NIL '(DYNAMIC LOCAL))))
(UNWIND-PROTECT
(PROGN (SYSTEM::%PUTD #:TEMP-3076 #:LETF-VALUE-3077)
(LIST (FUNCALL #'F) (FUNCALL 'F) (G)))
(SYSTEM::%PUTD #:TEMP-3076 #:NEW-3075))) ;
T
[6]>

Mais cela modifie effectivement le slot dynamique global
(symbol-function 'f), tout comme dans le cas du slot valeur:

(defvar *x* 1)
(defun f ()
*x*)
(let ((*x* 2))
(f))
--> 2

Yliur

unread,
Jan 12, 2012, 9:37:06 PM1/12/12
to
Le Thu, 12 Jan 2012 15:16:01 +0100
D'accord. Merci pour toutes ces informations :) .
0 new messages