【问题标题】:Common Lisp: shorthand to initialize a hash table with many entriesCommon Lisp:初始化具有许多条目的哈希表的简写
【发布时间】:2012-05-29 04:18:50
【问题描述】:

我正在寻找一种可能不冗长的可移植方式来初始化 Common Lisp 中的哈希表。例如。适用于常量哈希表的东西,也适用于预加载变量哈希。在 CLISP 中我正在使用:

(defconstant +my-map+ #S(HASH-TABLE :TEST FASTHASH-EQ
  (key1 . "value1")
  ...
  (keyN . "valueN")
))

但不幸的是,这种格式仅适用于 CLISP。

【问题讨论】:

  • 请注意,例如aiai.ed.ac.uk/~jeff/lisp/cl-pitfalls 警告不要将哈希表用作 defconstant 表单的值。
  • 谢谢大家。标准中似乎缺少此基本功能,必须以某种方式添加。与其引入新语法、模仿 Perl 或 PHP,不如编写一个包含 make-hash-table 并添加选项 :initial-contents 的宏,该选项与标准中支持的选项相同,例如 make-array?我认为这可能不会很有效,因为内容将由必须遍历的 alist 指定,但它至少与 Lisp 语法更一致。

标签: initialization constants hashtable common-lisp


【解决方案1】:

可以在读取时以编程方式构造哈希表:

(defvar *ht* #.(let ((ht (make-hash-table)))
                 (loop for (key . value) in
                       '((a . 1) (b . 2) (c . 3))
                       do (setf (gethash key ht) value))
                 ht))

(describe *ht*)

#. 用于读取时间评估。然后编译器会将哈希表转储到 FASL 文件中。

然后可以编译:

使用 SBCL:

* (compile-file "/tmp/test.lisp")

; compiling file "/private/tmp/test.lisp" (written 24 MAY 2012 10:08:49 PM):
; compiling (DEFVAR *HT* ...)
; compiling (DESCRIBE *HT*)

; /tmp/test.fasl written
; compilation finished in 0:00:00.360
#P"/private/tmp/test.fasl"
NIL
NIL
* (load *)

#<HASH-TABLE :TEST EQL :COUNT 3 {100299EA43}>
  [hash-table]

Occupancy: 0.2
Rehash-threshold: 1.0
Rehash-size: 1.5
Size: 16
Synchronized: no
T
* *ht*

#<HASH-TABLE :TEST EQL :COUNT 3 {100299EA43}>

将哈希表创建为函数:

(defun create-hashtable (alist
                         &key (test 'eql)
                         &aux (ht (make-hash-table :test test)))
  (loop for (key . value) in alist
        do (setf (gethash key ht) value))
  ht)

【讨论】:

  • 非常感谢莱纳!唯一的缺点是它有点冗长,但可以使用宏来帮助。我是一个 Lisp 初学者,不太擅长宏。无论如何,这是我的:(defmacro ini-hash-table (pairs) (let ((hash (make-hash-table :test 'equal))) (loop for (key value) on ,pairs by #'cddr do (setf (gethash key hash) value) ) hash))` 然后我做:(defvar *ht* #.(ini-hash-table '(a 1 b 2 c 3)))
  • @AntonioBonifati:当我怀疑编写函数而不是宏时。没有理由它应该是宏观的,是吗?
  • 是的,谢谢,我知道,一个很好的理由是宏更难编写和调试。但是如果我在这种情况下编写一个函数,我不能用#调用它。至少在 ECL 中它告诉我它是未定义的。我想这就是#的效果。也就是说,任何用户定义的函数在读取时都不可用。我想知道是否可以编写一个宏包装器,允许在添加 :initial-contents 关键字参数的同时完全配置 make-hash-table。那将“将此功能添加到标准中”:)
  • @Antonio Bonifati。如果在同一个文件中定义函数,则可以使用 eval-when 子句使其对编译器可用。
【解决方案2】:

Alexandria 具有alist-hash-table 函数,您可能会发现它很有用。

【讨论】:

    【解决方案3】:

    Serapeum 库有 dict:

    (dict :a 1 :b 2 :c 3)
    #<HASH-TABLE :TEST EQUAL :COUNT 3 {1008906D13}>
    

    您可以漂亮地打印哈希表:

    CL-USER> (toggle-pretty-print-hash-table)
    T
    CL-USER> (dict :a 1 :b 2 :c 3)
    (dict
      :A 1
      :B 2
      :C 3
     ) 
    

    这是一种可以读回的表示。

    我们也可以直接使用pretty-print-hash-table

    Serapeum 是一个高质量的图书馆。

    ps:我的CIEL 元包默认使dict 可用。

    【讨论】:

      【解决方案4】:

      哇,9 年前的一个问题得到了另一个答案。巧合的是,make-hash 是在提出这个问题 2 个月后出现的,并且通常对这种事情很有用。

      【讨论】:

        猜你喜欢
        • 2011-08-29
        • 2011-08-19
        • 1970-01-01
        • 2016-02-10
        • 2011-05-16
        • 1970-01-01
        • 1970-01-01
        • 2016-10-24
        • 2010-10-24
        相关资源
        最近更新 更多