Lisp 使用gensym和macrolet构建哈希表

Lisp 使用gensym和macrolet构建哈希表,lisp,common-lisp,clisp,gnu-common-lisp,Lisp,Common Lisp,Clisp,Gnu Common Lisp,我试图在读取时构建一个哈希表(以及其他操作)。我不希望哈希表具有全局作用域,所以我使用宏和gensym。在宏x中,我定义了一个宏s,它类似于setf,但在哈希表中定义了一个条目,而不是在某处定义符号。它爆炸了。我想我理解了错误消息,但是我如何使它工作呢 守则: #!/usr/bin/clisp -repl (defmacro x (&rest statements) (let ((config-variables (gensym))) `(macrolet ((s (pla

我试图在
读取时构建一个哈希表(以及其他操作)。我不希望哈希表具有全局作用域,所以我使用宏和
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

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

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

猜猜比尔到底想要什么

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

这是程序上的方法

  • 打开数据流
  • 把它读作一个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)
使用:

将其转换为宏:

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

(defmapping
  (fred 3)
  (barney 5))

猜猜比尔到底想要什么

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

这是程序上的方法

  • 打开数据流
  • 把它读作一个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)
使用:

将其转换为宏:

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

(defmapping
  (fred 3)
  (barney 5))


这看起来很复杂,我敢打赌你不需要这些。一个宏,一个小宏,读取数据的评估,单个字母的宏名称,…通常我会同意:这太复杂了。但这是更复杂的事情的一部分。我把它归结为一个小问题,这使得问题更容易提问。为什么要将哈希表编码为Lisp源代码?如果它只是一个静态哈希表,我不会。此哈希表中的一些值将是lambda表达式,在评估长期完成后的某个时间点,可能会调用这些表达式来设置(例如)fred的值。这看起来非常复杂,我敢打赌您不需要任何这些。一个宏,一个小宏,读取数据的评估,单个字母的宏名称,…通常我会同意:这太复杂了。但这是更复杂的事情的一部分。我把它归结为一个小问题,这使得问题更容易提问。为什么要将哈希表编码为Lisp源代码?如果它只是一个静态哈希表,我不会。此哈希表中的一些值将是lambda表达式,在评估长期完成后的某个时间点,可能会调用这些表达式来设置(例如)fred的值。非常有效,谢谢。我对
,',config variables
中两个逗号之间的单引号感到困惑。我拿出那句话,它就爆炸了。它在那里干什么?很好用,谢谢。我对
,',config variables
中两个逗号之间的单引号感到困惑。我拿出那句话,它就爆炸了。它在那里干什么?非常有启发性,谢谢。事实证明,我没有从配置文件中简单加载值。在这个配置文件中将有真实的实时LISP代码;弗雷德有时可能会被设置为3,有时会被设置为4,而在其他时间永远不会被设置。当表达式第一次求值时,fred根本不会被设置。散列中的一些值将是lambda表达式,它可能会根据一天中的时间和其他环境因素来设置fred。我在这里看到的代码示例,就像接触一门新的外语一样,帮助我更流利地使用这门语言。所以非常感谢你!事实证明,哈希表不会同时定义。我可能会把弗雷德今天定在15:23,威尔玛明天定在08:00,巴尼下周某个时候,贝蒂也许永远不会。@Mariposa的比尔·埃文斯:是什么触发了各种数值的实际计算?通常会存储一个函数,根据需要计算值。主要用于CLOS(或类似)对象。简化:在某个随机时间输入一个数字串。假设字符串是“31415926535”。我想看看这个字符串是否定义了fred。首先我检查它是否为3定义。然后是31岁。然后是314,以此类推。如果为其中的一个以上定义,则以后一个为准。为此,我走下一棵树;树上的节点可能指向哈希表中的lambda表达式。这些lambda表达式中的任何一个都可能(也可能不)在同一个哈希表中定义fred。Rainer,尽管我仍在使用S宏(以及用于从哈希表中提取值的G宏),但事实证明,您的建议在我的情况下非常恰当:这里还有其他宏我真的没有用处,由于您提到的原因,我正在删除,包括(*鼓轮*)整个宏X!再次感谢!非常有启发性,谢谢。事实证明,我没有从配置文件中简单加载值。在这个配置文件中将有真实的实时LISP代码;弗雷德有时可能会被设置为3,有时会被设置为4,而在其他时间永远不会被设置。当表达式第一次求值时,fred根本不会被设置。散列中的一些值将是lambda表达式,它可能会根据一天中的时间和其他环境因素来设置fred。我在这里看到的代码示例,就像接触一门新的外语一样,帮助我更流利地使用这门语言。所以非常感谢你!事实证明,哈希表不会同时定义。我可能会把弗雷德定在今天15:23,威尔玛定在明天08:00,巴尼·萨默蒂姆