построение хеш-таблицы с gensym и macrolet - PullRequest
1 голос
/ 25 декабря 2011

Я пытаюсь создать хеш-таблицу (среди других действий), пока read ing.Я не хочу, чтобы хеш-таблица имела глобальную область (пока), поэтому я делаю это с помощью макроса и gensym.Внутри макроса x я определяю макрос s, который похож на setf, но определяет запись в хеш-таблице вместо определения где-либо символа.Это взрывается.Мне кажется, я понимаю сообщение об ошибке, но как мне заставить его работать?

Код:

#!/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)

Вывод:

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

Ответы [ 2 ]

4 голосов
/ 25 декабря 2011

Просто угадайте, чего на самом деле может хотеть Билл.

Допустим, он хочет сопоставить некоторые ключи с некоторыми значениями в виде конфигурации в файле.

Вот процедурный способ.

  • открыть поток для данных
  • прочитать его как s-выражение
  • просмотреть данные и заполнить хеш-таблицу

Пример кода:

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

Использование:

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

Преимущества:

  • без макросов
  • данные не кодируются в исходном кодеи сгенерированный исходный код
  • без оценки (безопасность!) через EVAL необходимо
  • без раздувания объектного кода через макросы, которые расширяются до большего кода
  • функциональная абстракция
  • намного проще для понимания и отладки

В качестве альтернативы я написал бы макрос для чтения для { такой, что {(fred 3) (barney 5)} будет непосредственно считываться как хеш-таблица.


Если вы хотите иметь вычисляемые значения:

(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

Превращение в макрос:

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

(defmapping
  (fred 3)
  (barney 5))
3 голосов
/ 25 декабря 2011

В macrolet вы также определяете макрос, поэтому применяются обычные правила, то есть вы должны заключать выражения в кавычки, которые должны оцениваться во время выполнения. Как это:

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