2015-11-15 28 views
6

Quando compilo il seguente codice, SBCL si lamenta che g! -valore-valore e g! -unit non sono definiti. Non sono sicuro di come eseguire il debug di questo. Per quanto posso dire, il flatten sta fallendo.Macro che scrivono macro - Errore di compilazione

Quando l'appiattimento raggiunge la parte non quotata di defunits, sembra che l'intera parte venga trattata come un atomo. Suona corretto?

Di seguito utilizza il codice del libro 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 corso Lambda Utilities - Capitolo 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 corso Lambda - Capitolo 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

Per qualche motivo la funzione g! -symbol-p non viene compilata in Clozure Common Lisp Versione 1.11 (DarwinX8664)! –

+0

È qualcosa di simile a ciò che SBCL stava facendo? – SpyroSoft

risposta

6

Questa è una specie di complicato:

Problema: si assume che le espressioni backquote/virgola sono liste di pianura.

È necessario porsi questa domanda:

Qual è la rappresentazione di un'espressione backquote/virgola?

È una lista?

In realtà la rappresentazione completa non è specificata. Vedi qui: CLHS: Section 2.4.6.1 Notes about Backquote

Stiamo utilizzando SBCL. Vedere questo:

* (setf *print-pretty* nil) 

NIL 


* '`(a ,b) 

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

Quindi un'espressione virgola è rappresentato da una struttura di tipo SB-IMPL::COMMA. Gli sviluppatori di SBCL pensavano che questa rappresentazione fosse d'aiuto quando tali elenchi di backquote dovevano essere stampati dalla graziosa stampante.

Dal momento che i flatten tratta i strutture come gli atomi, non guardare dentro ...

Ma questa è la concreta rappresentazione di SBCL. Clozure CL fa qualcos'altro e LispWorks fa ancora qualcos'altro.

Clozure CL:

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

LispWorks:

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

debug

Dal momento che hai scoperto che in qualche modo flatten è stato coinvolto, la procedura successiva di debug sono:

Primo: tracciare la funzione flatten e vedere con quali dati viene chiamato e cosa restituisce.

Dato che non siamo sicuri di cosa siano effettivamente i dati, è possibile effettuare il comando INSPECT.

Un esempio il debug utilizzando 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 

Sopra chiama INSPECT sui dati di argomento. In Common Lisp, l'Inspector di solito è qualcosa in cui è possibile interagire in modo interattivo con le strutture dati inspect.

Come esempio stiamo chiamando flatten con un'espressione backquote:

* (flatten '`(a ,b)) 

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

Siamo nel ispettore interattivo. I comandi sono ora disponibili:

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

Così il comando 1 passeggiate nella struttura dei dati, ecco una lista.

> 1 

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

Passeggiata nel ulteriormente:

> 1 

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

Qui il ispettore ci dice che l'oggetto è una struttura di un certo tipo. Questo è quello che volevamo sapere.

Ora lasciare il ispettore utilizzando il comando q e la funzione flatten continua e restituisce un valore:

> q 

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

Grazie. Wow, è una struttura. Suppongo che nessuno abbia realizzato una libreria quasiquote portatile, vero? – SpyroSoft

+1

Questa è esattamente la spiegazione che stavo cercando per capire perché defmacro/g! la macro definita nel capitolo 3 di LOL (http://letoverlambda.com/index.cl/guest/chap3.html) non funziona! Grazie –

0

Per chiunque altro che sta cercando di ottenere defmacro! lavorare su SBCL, una soluzione temporanea a questo problema è brancolare all'interno della struttura unquote durante la procedura di appiattire ricorsivamente appiattire il suo contenuto:

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

Ma questo è piattaforma orribilmente dipendente. Se trovo un modo migliore, lo posterò.