IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Lisp Discussion :

Macros imbriquées - combinaison backquote comma-at


Sujet :

Lisp

  1. #1
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut Macros imbriquées - combinaison backquote comma-at
    Bonjour,

    Je rencontre quelques problèmes dans la définition d'une macro imbriquée. Je précise qu'il s'agit d'une imbrication un peu compliquée: la macro 1 définit la macro 2 et 3, et la macro 3 est définie à l'aide de la macro 2. Comme tout cela est un peu abstrait, voilà mon code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    (defmacro declare-monad (name alis) ;; macro 1, ex: (declare-monad parser ((>>= . parser>>=)  (return . parser-return)))
      (let* ((str-name (symbol-name name)) ;; "parser"
    	 (mac-name (intern (concatenate 'string "WITH-" str-name "-MONAD"))) ;; with-parser-monad
    	 (red-name (intern (concatenate 'string str-name "-REDUCE")))) ;; parser-reduce
        `(progn
           (defmacro ,mac-name (&body body)  ;; ex: (with-parser-monad (return #\a)) => (parser-return #\a)
    	 (let ((nbody (sublis ',alis body))) ;; substitution
    	   `(values ,@nbody))) ;; :-(
           (,mac-name ;; with-parser-monad : on définit donc la troisème macro dans le contexte de la deuxième.
    	(defmacro ,red-name (&rest mfncalls)
    	  (let ((tr (reduce (lambda (l r)
    			      (list ' >>= l r)) mfncalls))) ;; du coup >>= est remplacé par parser>>=
    	    `(values ,tr))))))) ;; :-(
    Mon problème est le suivant: à l'issue de chacune des deux macros "internes", je souhaite retourner le body en argument recalculé (respectivement nbody et tr) sans rajouter de parenthèses.
    Or
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    `(,@nbody) ;; rajoute des parenthèsess
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ,@nbody ;; s'interprète dans le contexte de la première backquote => undeclared free variable nbody
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ',@body ;; idem => undeclare free variable nbdody
    Comme solution je n'ai trouvé que:
    mais c'est tellement moche que je pense que qqc m'échappe.

    Des idées?
    Merci d'avance

  2. #2
    Expert confirmé
    Homme Profil pro
    Développeur informatique en retraite
    Inscrit en
    Avril 2008
    Messages
    2 101
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique en retraite

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 101
    Points : 5 849
    Points
    5 849
    Par défaut
    Citation Envoyé par stendhal666 Voir le message
    Je rencontre quelques problèmes dans la définition d'une macro imbriquée. Je précise qu'il s'agit d'une imbrication un peu compliquée: la macro 1 définit la macro 2 et 3, et la macro 3 est définie à l'aide de la macro 2. Comme tout cela est un peu abstrait, voilà mon code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    (defmacro declare-monad (name alis) ;; macro 1, ex: (declare-monad parser ((>>= . parser>>=)  (return . parser-return)))
      (let* ((str-name (symbol-name name)) ;; "parser"
    	 (mac-name (intern (concatenate 'string "WITH-" str-name "-MONAD"))) ;; with-parser-monad
    	 (red-name (intern (concatenate 'string str-name "-REDUCE")))) ;; parser-reduce
        `(progn
           (defmacro ,mac-name (&body body)  ;; ex: (with-parser-monad (return #\a)) => (parser-return #\a)
    	 (let ((nbody (sublis ',alis body))) ;; substitution
    	   `(values ,@nbody))) ;; :-(
           (,mac-name ;; with-parser-monad : on définit donc la troisème macro dans le contexte de la deuxième.
    	(defmacro ,red-name (&rest mfncalls)
    	  (let ((tr (reduce (lambda (l r)
    			      (list ' >>= l r)) mfncalls))) ;; du coup >>= est remplacé par parser>>=
    	    `(values ,tr))))))) ;; :-(
    Effectivement! Avec le code, c'est beaucoup plus clair!

    euh... Oui mais non. En fait, ce n'est pas complètement trivial... J'espère juste que ce sera encore clair pour toi lorsque tu reliras ces lignes dans quelques semaines (ou mois ou années!).


    Mon problème est le suivant: à l'issue de chacune des deux macros "internes", je souhaite retourner le body en argument recalculé (respectivement nbody et tr) sans rajouter de parenthèses.
    Comme solution je n'ai trouvé que:
    mais c'est tellement moche que je pense que qqc m'échappe.
    Une première remarque: le macro-caractère backquote est une facilité d'écriture destinée à remplacer l'écriture standard... où l'écriture standard (et un peu "lourde") est celle qui utilise les fonctions "cons" "list" "append" "nconc" etc.

    Si tu sais précisément ce que tu veux faire à l'aide de ces fonctions, alors tu peux simplifier l'écriture avec la backquote, mais je pense qu'il est toujours intéressant de bien en comprendre le sens... c'est-à-dire de commencer par l'écriture "lourde"!

    Ta première macro "declare-monad" génère une liste qui commence par le symbole progn, lequel te permet de dire que tu veux générer une liste d'instructions (2 instructions) à exécuter en séquence.
    Bravo! La forme spéciale "progn" est justement dédiée à cet usage!

    La 2ème macro (dont le nom est le contenu de la variable "mac-name" (dans le contexte de l'appel à "declare-monad")) reçoit un argument-liste, nommé "body" et qui est destiné (si son nom est bien choisi) à recevoir une liste d'instructions.
    Personne sauf toi ne peut décider ce qu'il faut faire de cette liste d'instructions!
    Peut-être faudrait-il les évaluer et faire la somme de toutes les valeurs retournées (bien que j'en doute un peu!).
    Peut-être faut-il les exécuter en parallèle et retourner toutes les valeurs retournées (comme pour le classique exemple d'une fonction de division qui retourne à la fois le quotient et le reste), auquel cas il est effectivement judicieux d'utiliser la forme "values" (exactement comme tu as fait).
    Peut-être mon intuition (féminine) m'indique qu'il faudrait juste les exécuter en séquence et retourner la valeur retournée par la dernière, auquel cas il serait judicieux d'utiliser la forme "progn", exactement comme tu as fait pour la première macro "declare-monad", à savoir:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    (defmacro ,mac-name (&body body)  ;; ex: (with-parser-monad (return #\a)) => (parser-return #\a)
      (let ((nbody (sublis ',alis body))) ;; substitution
        (cons 'progn nbody)))
    qui peut être simplifié en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    (defmacro ,mac-name (&body body)  ;; ex: (with-parser-monad (return #\a)) => (parser-return #\a)
      (let ((nbody (sublis ',alis body))) ;; substitution
        `(progn ,@nbody)))
    Concernant la 3ème macro, c'est encore plus toi le plus habilité à décider ce qu'il faut en faire!
    Vu que son argument "mfncalls" n'est pas porteur d'une sémantique particulièrement explicite...
    Mais vu ce que retourne le "reduce" sur une liste "bête":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     (reduce (lambda (l r) (list ' >>= l r)) '(a b c d))
    (>>= (>>= (>>= a b) c) d)
    on est en droit de penser que cette liste retournée est un simple appel fonctionnel (l'appel de ">>" (remplacé par un truc plus spécifique) appliqué à 2 arguments).
    En conséquence, la macro pourrait probablement retourner ce simple appel fonctionnel (ce que semble demander d'ailleurs plus haut dans ton message), soit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    (defmacro ,red-name (&rest mfncalls)
      (let ((tr (reduce (lambda (l r) (list ' >>= l r)) mfncalls))) ;; où >>= est remplacé par parser>>=
        tr))
    Que l'on peut même simplifier en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    (defmacro ,red-name (&rest mfncalls)
      (reduce (lambda (l r) (list ' >>= l r)) mfncalls)) ;; où >>= est remplacé par parser>>=
    Au cas où ce ne serait pas parfaitement clair, il faut bien penser que l'appel d'une macro dans un contexte appelant lexical est destiné à être totalement supprimé et remplacé (grâce à un violent "nconc"), toujours dans ce contexte d'appel lexical, par autre chose. Cette autre chose étant la liste retournée par l'évaluation du corps de la macro...
    J'espère que c'est beaucoup plus clair

  3. #3
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    Merci pour cette réponse détaillée!
    Les simplifications que tu proposes rendent déjà le code plus clair. Et si un (une?) lispien expert autorise des formules qui me semblent un peu barbare comme `(values ,@nbody), alors il n'y a pas de raison de ne pas me le permettre.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Toutes versions] Quel est le meilleures choix ? (Macro imbriquées)
    Par skk201 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 13/01/2014, 12h14
  2. macro imbriquées et arrêt ed la macro principale.
    Par mohamed_ dans le forum Macro
    Réponses: 5
    Dernier message: 29/07/2010, 21h46
  3. Stopper plusieurs macros imbriquées
    Par Fr33dom dans le forum Macros et VBA Excel
    Réponses: 0
    Dernier message: 04/02/2010, 09h02
  4. [XL2003]gestion macros imbriquées..
    Par tony020422 dans le forum Macros et VBA Excel
    Réponses: 0
    Dernier message: 07/08/2009, 14h25
  5. [BUG] Macros "imbriquées"
    Par ML0808 dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 07/04/2008, 14h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo