2011-12-25 1 views
1

Je suis en train de construire une table de hachage (entre autres actions) tandis que read ing. Je ne veux pas que la table de hachage ait une portée globale (encore), donc je le fais avec une macro et gensym. À l'intérieur de la macro x, je définis une macro s qui est similaire à setf, mais définit une entrée dans une table de hachage au lieu de définir un symbole quelque part. Ça explose. Je pense que je comprends le message d'erreur, mais comment puis-je le faire fonctionner?construire une table de hachage avec gensym et macrolet

Le code:

#!/usr/bin/clisp -repl 

(defmacro x (&rest statements) 
    (let ((config-variables (gensym))) 
    `(macrolet ((s (place value) 
        (setf (gethash 'place ,config-variables) value))) 
     (let ((,config-variables (make-hash-table :test #'eq))) 
     (progn ,@statements) 
     ,config-variables)))) 

(defun load-config() 
    (let ((config-file-tree (read *standard-input*))) 
    (eval config-file-tree))) 

(defun load-test-config() 
    (with-input-from-string (*standard-input* "(x (s fred 3) (s barney 5))") 
    (load-config))) 

(load-test-config) 

La sortie:

*** - LET*: variable #:G12655 has no value 
The following restarts are available: 
USE-VALUE  :R1  Input a value to be used instead of #:G12655. 
STORE-VALUE :R2  Input a new value for #:G12655. 
SKIP   :R3  skip (LOAD-TEST-CONFIG) 
STOP   :R4  stop loading file /u/asterisk/semicolon/build.l/stackoverflow-semi 
+0

Cela semble si compliqué que je serais prêt à parier que vous n'avez pas besoin quoi que ce soit de cette. Une macro, un macrolet, EVALuation des données de lecture, des noms de macro à une seule lettre, ... –

+0

D'habitude, je suis d'accord: c'est trop compliqué. Mais cela fait partie de quelque chose de plus impliqué. Je l'ai distillé à une situation de bouchée qui a rendu la question plus facile à poser. –

+0

Pourquoi voudriez-vous encoder une table de hachage comme code source Lisp? –

Répondre

3

Dans un macrolet vous êtes aussi bien la définition d'une macro, de sorte que les règles habituelles applicables, à savoir que vous avez à BACKQUOTE expressions, qui sont à évaluer à l'exécution. Comme ceci:

(defmacro x (&rest statements) 
    (let ((config-variables (gensym))) 
    `(macrolet ((s (place value) 
       `(setf (gethash ',place ,',config-variables) ,value))) 
     (let ((,config-variables (make-hash-table :test #'eq))) 
     (progn ,@statements) 
     ,config-variables)))) 
+0

Fonctionne parfaitement, merci. Je suis intrigué par la simple citation entre les deux virgules dans ', ', config-variables'. J'ai sorti cette citation unique et ça a explosé. Que fait-il là? –

+0

Oups. Deviner. Duh. –

4

Juste deviner ce que Bill pourrait vraiment vouloir. Disons qu'il veut un mappage de certaines clés à certaines valeurs en tant que configuration dans un fichier.

Voici la procédure.

  • ouvrir un flux de données
  • lire comme une s-expression
  • marcher les données et remplir une table de hachage table

Exemple code:

(defun read-mapping (&optional (stream *standard-input*)) 
    (destructuring-bind (type &rest mappings) (read stream) 
    (assert (eq type 'mapping)) 
    (let ((table (make-hash-table))) 
     (loop for (key value) in mappings 
      do (setf (gethash key table) value)) 
     table))) 

(defun load-config() 
    (read-mapping)) 

(defun load-test-config() 
    (with-input-from-string (*standard-input* "(mapping (fred 3) (barney 5))") 
    (load-config))) 

(load-test-config) 

Utilisation:

CL-USER 57 > (load-test-config) 
#<EQL Hash Table{2} 402000151B> 

CL-USER 58 > (describe *) 

#<EQL Hash Table{2} 402000151B> is a HASH-TABLE 
BARNEY  5 
FRED  3 

Avantages:

  • aucune macro
  • données ne soit pas encodées dans le code source et Generated code source
  • aucune évaluation (! De sécurité) par l'intermédiaire EVAL nécessaires
  • pas de code objet ballonnement via des macros qui sont en expansion à un plus grand code
  • abstraction fonctionnelle
  • beaucoup plus facile à comprendre et déboguer

Sinon j'écrirais une macro de lecture pour { de telle sorte que {(fred 3) (barney 5)} serait directement lu comme une table de hachage.


Si vous voulez avoir des valeurs calculées:

(defun make-table (mappings &aux (table (make-hash-table))) 
    (loop for (key value) in mappings 
     do (setf (gethash key table) (eval value))) 
    table) 

CL-USER 66> (describe (make-table '((fred (- 10 7)) (barney (- 10 5))))) 

#<EQL Hash Table{2} 4020000A4B> is a HASH-TABLE 
BARNEY  5 
FRED  3 

tournant qui dans une macro:

(defmacro defmapping (&body mappings) 
    `(make-table ',mappings)) 

(defmapping 
    (fred 3) 
    (barney 5)) 
+0

Le plus instructif, merci. Il s'avère que je ne fais pas une simple charge de valeurs à partir d'un fichier de configuration. Il y aura un vrai code LISP en direct dans ce fichier de configuration; fred peut parfois être réglé sur 3, être réglé sur 4 parfois, et ne jamais être réglé à d'autres moments. Lorsque l'expression est évaluée pour la première fois, fred ne sera pas définie du tout. Certaines des valeurs dans le hachage seront des expressions lambda qui pourraient définir fred en fonction de l'heure de la journée et d'autres considérations environnementales. –

+1

@Bill Evans à Mariposa: voir mon édition ci-dessus ... –

+0

Rainer, je suis encore nouveau à ce sujet. Les échantillons de code que je vois ici, comme toute exposition à une nouvelle langue étrangère, m'aident à maîtriser la langue. Alors merci beaucoup! Il s'avère que la table de hachage ne sera pas définie en même temps. Je pourrais mettre fred aujourd'hui à 15h23, wilma demain à 08h00, barney la semaine prochaine, et betty peut-être jamais. –

Questions connexes