2015-11-15 1 views
6

Lorsque je compile le code suivant, SBCL se plaint que g! -unit-value et g! -unit ne sont pas définis. Je ne suis pas sûr de savoir comment déboguer cela. Autant que je sache, aplatir échoue. Lorsque l'aplatissement atteint la partie non citée des diffunits, il semble que la partie entière soit traitée comme un atome. Est-ce que cela semble correct?Macros qui écrivent des macros - Erreur de compilation

Ce qui suit utilise le code du livre Let over Lambda:

Paul Graham Utilities

(defun symb (&rest args) 
    (values (intern (apply #'mkstr args)))) 

(defun mkstr (&rest args) 
    (with-output-to-string (s) 
    (dolist (a args) (princ a s)))) 

(defun group (source n) 
    (if (zerop n) (error "zero length")) 
    (labels ((rec (source acc) 
      (let ((rest (nthcdr n source))) 
       (if (consp rest) 
        (rec rest (cons (subseq source 0 n) acc)) 
        (nreverse (cons source acc)))))) 
    (if source (rec source nil) nil))) 

(defun flatten (x) 
    (labels ((rec (x acc) 
      (cond ((null x) acc) 
        ((atom x) (cons x acc)) 
        (t (rec (car x) (rec (cdr x) acc)))))) 
    (rec x nil))) 

Let Over Lambda Utilities - Chapitre 3

(defmacro defmacro/g! (name args &rest body) 
    (let ((g!-symbols (remove-duplicates 
       (remove-if-not #'g!-symbol-p 
           (flatten body))))) 
    `(defmacro ,name ,args 
     (let ,(mapcar 
       (lambda (g!-symbol) 
       `(,g!-symbol (gensym ,(subseq 
             (symbol-name g!-symbol) 
             2)))) 
       g!-symbols) 
     ,@body)))) 

(defun g!-symbol-p (symbol-to-test) 
    (and (symbolp symbol-to-test) 
     (> (length (symbol-name symbol-to-test)) 2) 
     (string= (symbol-name symbol-to-test) 
       "G!" 
       :start1 0 
       :end1 2))) 

(defmacro defmacro! (name args &rest body) 
    (let* ((o!-symbols (remove-if-not #'o!-symbol-p args)) 
     (g!-symbols (mapcar #'o!-symbol-to-g!-symbol o!-symbols))) 
    `(defmacro/g! ,name ,args 
     `(let ,(mapcar #'list (list ,@g!-symbols) (list ,@o!-symbols)) 
      ,(progn ,@body))))) 

(defun o!-symbol-p (symbol-to-test) 
    (and (symbolp symbol-to-test) 
     (> (length (symbol-name symbol-to-test)) 2) 
     (string= (symbol-name symbol-to-test) 
       "O!" 
       :start1 0 
       :end1 2))) 

(defun o!-symbol-to-g!-symbol (o!-symbol) 
    (symb "G!" (subseq (symbol-name o!-symbol) 2))) 

Let Over Lambda - Chapitre 5

(defun defunits-chaining (u units prev) 
    (if (member u prev) 
     (error "~{ ~a~^ depends on~}" 
      (cons u prev))) 
    (let ((spec (find u units :key #'car))) 
    (if (null spec) 
     (error "Unknown unit ~a" u) 
     (let ((chain (second spec))) 
      (if (listp chain) 
       (* (car chain) 
       (defunits-chaining 
        (second chain) 
        units 
        (cons u prev))) 
       chain))))) 

(defmacro! defunits (quantity base-unit &rest units) 
    `(defmacro ,(symb 'unit-of- quantity) 
     (,g!-unit-value ,g!-unit) 
    `(* ,,g!-unit-value 
     ,(case ,g!-unit 
       ((,base-unit) 1) 
       ,@(mapcar (lambda (x) 
          `((,(car x)) 
           ,(defunits-chaining 
           (car x) 
           (cons 
           `(,base-unit 1) 
           (group units 2)) 
           nil))) 
          (group units 2)))))) 
+0

Pour une raison quelconque, la fonction g! -symbol-p ne se compile pas dans Clozure Common Lisp Version 1.11 (DarwinX8664)! –

+0

Est-ce que c'est quelque chose de similaire à ce que faisait SBCL? – SpyroSoft

Répondre

6

C'est un peu délicat:

Problème: vous assumez que les expressions backquote/virgule sont des listes simples.

Vous devez vous poser cette question:

Quelle est la représentation d'une expression backquote/virgule?

Est-ce une liste?

En fait, la représentation complète n'est pas spécifiée. Voir ici: CLHS: Section 2.4.6.1 Notes about Backquote

Nous utilisons SBCL. Voir ceci:

* (setf *print-pretty* nil) 

NIL 


* '`(a ,b) 

(SB-INT:QUASIQUOTE (A #S(SB-IMPL::COMMA :EXPR B :KIND 0))) 

donc une expression de virgule est représentée par une structure de type SB-IMPL::COMMA. Les développeurs de SBCL ont pensé que cette représentation aide quand de telles listes de backquote doivent être imprimées par la jolie imprimante.

Depuis votre flatten traite des structures comme des atomes, il ne sera pas regarder à l'intérieur ...

Mais ceci est la représentation spécifique de SBCL. Clozure CL fait autre chose et LispWorks fait encore autre chose.

Clozure CL:

? '`(a ,b) 
(LIST* 'A (LIST B)) 

LispWorks:

CL-USER 87 > '`(a ,b) 
(SYSTEM::BQ-LIST (QUOTE A) B) 

Debugging

Depuis que vous avez trouvé que flatten en quelque sorte a été impliqué, les prochaines étapes de mise au point sont:

Tout d'abord: tracer la fonction flatten et voir avec quelles données il est appelé et ce qu'il renvoie.

Puisque nous ne sommes pas sûr de ce que les données sont réellement, on peut INSPECT il.

Un exemple de débogage en utilisant SBCL:

* (defun flatten (x)                       
    (inspect x)                        
    (labels ((rec (x acc)                      
       (cond ((null x) acc)                   
        ((atom x) (cons x acc))                 
        (t (rec (car x) (rec (cdr x) acc))))))             
     (rec x nil))) 
STYLE-WARNING: redefining COMMON-LISP-USER::FLATTEN in DEFUN 

FLATTEN 

Au-dessus appelle INSPECT sur les données de l'argument. Dans Common Lisp, l'inspecteur est généralement quelque chose où l'on peut interactivement inspecter structures de données.

À titre d'exemple, nous faisons appel flatten avec une expression backquote:

* (flatten '`(a ,b)) 

The object is a proper list of length 2. 
0. 0: SB-INT:QUASIQUOTE 
1. 1: (A ,B) 

Nous sommes dans l'inspecteur interactif. Les commandes sont maintenant disponibles:

> help 

help for INSPECT: 
    Q, E  - Quit the inspector. 
    <integer> - Inspect the numbered slot. 
    R   - Redisplay current inspected object. 
    U   - Move upward/backward to previous inspected object. 
    ?, H, Help - Show this help. 
    <other>  - Evaluate the input as an expression. 
Within the inspector, the special variable SB-EXT:*INSPECTED* is bound 
to the current inspected object, so that it can be referred to in 
evaluated expressions. 

Ainsi, la commande 1 marche dans la structure de données, voici une liste.

> 1 

The object is a proper list of length 2. 
0. 0: A 
1. 1: ,B 

Promenade dans plus:

> 1 

The object is a STRUCTURE-OBJECT of type SB-IMPL::COMMA. 
0. EXPR: B 
1. KIND: 0 

Ici, l'inspecteur nous dit que l'objet est une structure d'un certain type. C'est ce que nous voulions savoir.

Nous quittons maintenant l'inspecteur en utilisant la commande q et la fonction flatten continue et retourne une valeur:

> q 

(SB-INT:QUASIQUOTE A ,B) 
+0

Merci. Wow, c'est une structure. Je suppose que personne n'a créé une bibliothèque de quasiquotes portable, n'est-ce pas? – SpyroSoft

+1

C'est précisément l'explication que je cherchais à comprendre pourquoi le defmacro/g! macro défini dans le chapitre 3 de LOL (http://letoverlambda.com/index.cl/guest/chap3.html) ne fonctionne pas! Merci –

0

Pour toute autre personne qui tente d'obtenir defmacro! de travailler sur SBCL, une solution temporaire à ce problème est de tâtonner dans la structure unquote pendant la procédure Aplatir récursive aplatir son contenu:

(defun flatten (x) 
    (labels ((flatten-recursively (x flattening-list) 
      (cond ((null x) flattening-list) 
        ((eq (type-of x) 'SB-IMPL::COMMA) (flatten-recursively (sb-impl::comma-expr x) flattening-list)) 
        ((atom x) (cons x flattening-list)) 
        (t (flatten-recursively (car x) (flatten-recursively (cdr x) flattening-list)))))) 
    (flatten-recursively x nil))) 

Mais cette plate-forme est horriblement dépendante. Si je trouve un meilleur moyen, je l'afficherai.