如何正确地遍历符号作为创建散列 table 的键?

How do I correctly iterate through symbols as keys for creating a hash table?

简而言之:(gethash 'PARIS pandemic-hash-table) 返回 nil,尽管 'PARIS 是 table 中的一个键;这似乎与 hash-table 创建期间的符号 quoting/evaluation 在某种程度上有关,但我无法弄清楚。

我正在玩图形搜索(在棋盘游戏 Pandemic 中测试城市之间的最短路线;只是为了好玩 - 试图以比 'has the most edges' 更复杂的方式找到最佳研究实验室布置) .我正在使用散列 table 来保存路由数据(节点和边缘),并且需要输入数据作为初步数据:

(defvar *nodes* '('San-Francisco 'Chicago 'Atlanta 'Washington 'Montreal 'New-York 'Madrid 'Paris 'London 'Essen 'Milan 'St-Petersburg))

(defvar *edges* '(('Chicago 'St-Petersburg)
                  ('San-Francisco 'Atlanta 'Montreal)
                  ('Chicago 'Washington)
                  ('Atlanta 'Montreal 'New-York)
                  ('Chicago 'Washington 'New-York)
                  ('Montreal 'Washington 'Madrid 'London)
                  ('New-York 'London 'Paris)
                  ('Madrid 'Essen 'London 'Milan)
                  ('Madrid 'Essen 'London 'New-York)
                  ('London 'Paris 'Milan 'St-Petersburg)
                  ('Paris 'Essen)
                  ('Essen 'Chicago)))

(defvar *pandemic-node-hash* (make-hash-table))

(loop for node in *nodes*
      for edges in *edges*
      do (setf (gethash node *pandemic-node-hash*) edges))

如果我查看生成的哈希 table:

CL-USER> (loop for key being the hash-keys of *pandemic-node-hash*
               do (print key))

'SAN-FRANCISCO 
... ;other keys removed for brevity
'PARIS  
NIL

所以它使 table(边缘显示类似),但是,(gethash 'PARIS *pandemic-node-hash*) returns nil。如果我然后直接添加另一个 'PARIS 节点 (setf (gethash 'paris *pandemic-node-hash*) 'somevalue),并检查密钥,我得到:

(loop for key being the hash-keys of *pandemic-node-hash*
               do (print key))

'other keys
'PARIS
PARIS 
NIL

所以,问题 与初始散列 table 创建循环中符号('PARIS 和朋友)的求值有关,但我不太清楚发生了什么或如何正确地做到这一点。我猜 node 评估为 un 评估符号,将其传递给 gethash ...但什么是正确的方法?肯定不是(评估节点)?反引号列表,在符号前面用逗号? (呃)

为什么不

(dolist (node *nodes*)
  (dolist (edges *edges*)
    (setf (gethash node *pandemic-node-hash*) edges)))

但是你也用双引号引用了你的符号(正如其他人评论的那样)
为什么:

'(('Chicago 'St-Petersburg) ...)

什么时候应该只是这个(不要引用列表和每个符号)

'((Chicago St-Petersburg) ...)

如果您评估:*edges*

,您会看到这个

记住:'foo(quote foo) 的缩写形式。这是一个包含两个元素的列表:符号 CL:QUOTE 和符号 FOO.

 (defun show-it (arg)
   (print (list arg (type-of arg)))
   (values))

上面是这个答案的一个较小的辅助函数。

CL-USER 37 > (show-it 'hamburg)

(HAMBURG SYMBOL) 

上面显示函数看到的是符号 hamburg 而不是变量 hamburg.

的值

不是

CL-USER 38 > (show-it '('hamburg))

(((QUOTE HAMBURG)) CONS) 

上图:该函数看到一个嵌套列表,其中一个列表以 quote 作为符号。

注:((quote hamburg))可以简写为('hamburg)

CL-USER 39 > (show-it (first '('hamburg)))

((QUOTE HAMBURG) CONS) 

以上:如果我们得到第一个元素,我们得到带有quote符号的列表。

更好

CL-USER 40 > (show-it '(hamburg))

((HAMBURG) CONS) 

上面提供了一个包含一个符号的列表,符号 hamburg.

CL-USER 41 > (show-it (first '(hamburg)))

(HAMBURG SYMBOL) 

上面得到第一个元素,也就是符号hamburg.

评价

确保您了解 Lisp 中的求值。 quote 阻止对整个引用表达式及其所有级别的评估。因此,在引用列表中引用内容是没有意义的。

在函数调用中传递参数时,引号用于防止对列表和符号求值。因此 quote 创建文字数据:文字符号、文字列表、文字向量等

因此引用是一种代码机制,而不是数据机制。

代码:

(first '(hamburg)) vs. (first (hamburg))

getting the first element of a literal list vs.
getting the first element of the result
of calling the function `hamburg`.

数据:

(paris hamburg berlin rome)  vs. ('paris 'hamburg 'berlin 'rome) 

A list of city names vs. a list of city names,
each nested in a list (quote ...)

因此:

('paris 'hamburg 'berlin 'rome) 没有意义。