【问题标题】:How do I correctly iterate through symbols as keys for creating a hash table?如何正确迭代符号作为创建哈希表的键?
【发布时间】:2021-10-06 19:58:37
【问题描述】:

简而言之:(gethash 'PARIS pandemic-hash-table) 返回 nil,尽管 'PARIS 是表中的键;这似乎与以某种方式在哈希表创建期间引用/评估符号有关,但我无法弄清楚。

我正在玩图形搜索(在棋盘游戏 Pandemic 中测试城市之间的最短路线;只是为了好玩 - 尝试以比“拥有最多边缘”更复杂的方式找到最佳研究实验室位置)。我正在使用哈希表来保存路由数据(节点和边),并且需要输入数据作为初步数据:

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

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

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

所以它正在制作表格(并且边缘显示类似),但是,(gethash 'PARIS *pandemic-node-hash*) 返回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

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

【问题讨论】:

  • (defvar *nodes* '('San-Francisco 'Chicago ;;...)) -- 为什么要同时引用列表和符号?只需(defvar *nodes* '(San-Francisco Chicago ;;...))
  • 当然,说出来就很简单!我不知何故认为你需要引用符号来传递它们,即使我不会写 ('3 '4 '5) 等。谢谢。
  • 你需要引用符号来传递文字符号 -> 这样一个符号就不会被评估。但是您已经在列表中有符号。如果你调用(foo (first *nodes*)),Lisp 将评估(first *nodes*),但不会再次评估结果。
  • '( ... ) 内,不会评估任何包含的数据,因此不需要引用任何数据以防止评估。

标签: lisp common-lisp


【解决方案1】:

记住:'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) 毫无意义。

【讨论】:

    【解决方案2】:

    为什么不

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

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

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

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

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

    如果你评估,你会看到这个:*edges*

    【讨论】:

    • 是的,删除双引号解决了我的问题!关于您的建议:这不是要遍历所有边的每个节点(最终分配与每个节点的值相同的“最后一条边”)吗?
    • 您的嵌套 do 循环与 OP 的 loop 形式不同。使用loop,由for 引入的变量是并行迭代的。考虑:(loop for x in '(a b c) for y in '(1 2 3) collecting (list x y)) --> ((A 1) (B 2) (C 3)) 与...
    • ...(let ((result '())) (dolist (x '(a b c)) (dolist (y '(1 2 3)) (push (list x y) result))) (reverse result)) --> ((A 1) (A 2) (A 3) (B 1) (B 2) (B 3) (C 1) (C 2) (C 3))
    猜你喜欢
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2020-12-13
    • 1970-01-01
    • 2012-02-06
    • 2023-04-07
    相关资源
    最近更新 更多