1

我正在尝试在reading 时构建一个哈希表(以及其他操作)。我不希望哈希表具有全局范围(还),所以我使用宏和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
4

2 回答 2

4

只是猜测比尔可能真正想要什么。

假设他想要从某些键到某些值的映射作为文件中的配置。

这是程序的方式。

  • 打开数据流
  • 将其读为 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))
于 2011-12-25T14:12:08.193 回答
3

在一个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))))
于 2011-12-25T13:09:44.703 回答