【问题标题】:building a hash table with gensym and macrolet使用 gensym 和 macrolet 构建哈希表
【发布时间】:2011-12-25 12:21:40
【问题描述】:

我正在尝试在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

【问题讨论】:

  • 这看起来很复杂,我敢打赌你不需要任何东西。宏,macrolet,读取数据的 EVALuation,单字母宏名称,...
  • 通常我会同意:它太复杂了。但这是更复杂的事情的一部分。我把它提炼成一口大小的情况,使问题更容易提出。
  • 为什么要将哈希表编码为 Lisp 源代码?
  • 如果它只是一个静态哈希表,我不会。此哈希表中的某些值将是 lambda 表达式,在 eval 长时间完成后的某个时间点,可能会调用它们来设置(例如)fred 的值。

标签: lisp common-lisp clisp gnu-common-lisp


【解决方案1】:

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

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

这是程序方式。

  • 打开数据流
  • 将其读取为 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))

【讨论】:

  • 最具启发性,谢谢。事实证明,我并没有从配置文件中简单地加载值。此配置文件中会有真实的 LISP 代码; fred 有时可能会设置为 3,有时会设置为 4,而其他时候则永远不会设置。当表达式第一次被评估时,fred 根本不会被设置。哈希中的一些值将是 lambda 表达式,可能会根据一天中的时间和其他环境因素设置 fred。
  • Rainer,我还是新手。我在这里看到的代码示例,就像接触一门新的外语一样,帮助我更加流利地使用该语言。所以非常感谢!事实证明,哈希表不会同时被定义。我可能会安排今天 15:23 的弗雷德,明天 08:00 的威尔玛,下周某个时间巴尼,而贝蒂可能永远不会。
  • @Bill Evans at Mariposa:是什么触发了实际计算各种值?通常人们会存储一个函数,该函数根据需要计算值。大多数情况下,这与 CLOS(或类似)对象一起使用。
  • 简化:一串数字在某个随机时间出现。假设字符串是“31415926535”。我想看看,对于这个字符串,是否定义了 fred。首先,我检查它是否定义为 3。然后是 31。然后是 314,依此类推。如果为其中一个以上定义,则后一个优先。为此,我走下一棵树;树上的一个节点可能指向哈希表中的一个 lambda 表达式。这些 lambda 表达式中的任何一个都可能(或可能不)定义 fred,同样在同一个哈希表中。
  • Rainer,虽然我仍在使用 S 宏(以及用于从哈希表中提取值的 G 宏),但事实证明,您的建议在我的情况下非常中肯:还有其他这里的宏我真的没有用,并且出于您提到的原因而删除,包括(*鼓卷*)整个宏X!再次感谢!
【解决方案2】:

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

【讨论】:

  • 完美运行,谢谢。我对,',config-variables 中两个逗号之间的单引号感到困惑。我把那个单引号拿出来,它爆炸了。它在那里做什么?
猜你喜欢
  • 1970-01-01
  • 2011-09-05
  • 2015-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
  • 2011-02-27
  • 2013-03-21
相关资源
最近更新 更多