lisp 的问题和 do 构造与 itterating

problems with lisp and do construct with itterating

首先,我遇到了这个问题。我生成的代码不是遍历每个单词,而是遍历整个传递的参数。我正在使用 do 循环将信息传递到散列 table.

(defun set_isa (partOfSpeech &rest words)

(do ((wordVar words))
((null wordVar) nil) 

(putp partOfSpeech word-dict wordVar)
(setf wordVar (cdr wordVar))))

有了这个,我使用跟踪得到了这个结果

(set_isa 'Verb 'Talk 'Run 'jump )

1. Trace: (SET_ISA 'VERB 'TALK 'RUN 'JUMP)
1. Trace: SET_ISA ==> NIL
NIL

当我调用散列table时,它只添加最后传递的参数

#S(HASH-TABLE :TEST FASTHASH-EQL (VERB . (JUMP)))

欢迎来到 SO。我发现我们的问题存在一些问题,希望能给您一些提示。

缩进

请正确缩进您的代码。这将导致代码更具可读性,并增加其他人可以帮助您的可能性。

(defun set_isa (partOfSpeech &rest words)
  "Put an understandable docstring here!"
  (do ((wordVar words))
      ((null wordVar) nil) 
    (putp partOfSpeech word-dict wordVar)
    (setf wordVar (cdr wordVar))))

您会找到更多关于风格的建议 here. See also the Info tab 参考集。

可运行的例子

在我们的函数中 putp 没有定义。所以我无法 运行 你的代码并查看确切的错误等。请始终提供完整的示例并清楚地描述你的期望和你得到的东西。 hash-table 的部分根本不清楚。它从何而来?它在我们的代码中是如何使用的?

做循环

查看初学者参考以了解 do 及其相关语法的正确语法。如果您想使用 do 遍历列表,请尝试 dolist

(dolist (item '(a b c d))
  (print item))

使用 do 你可以用这个结构达到同样的效果:

(do ((items '(a b c d) (rest items)))
    ((null items))
  (print (first items)))

一开始我似乎很难把括号弄对,但如果你理解了它背后的逻辑,事情就会变得更容易。您不需要 setf 部分,因为 do 会处理它。

因此,了解此处发生的事情的方法是注释您的代码,以便它告诉您它在做什么。这似乎是一种古老的调试方式,但在像 CL 这样的动态会话语言中,这是一种非常好的方法。这是你的函数的一个版本,它使用了更传统的事物名称,还使用了传统的缩进,以及你丢失的代码的存根,使它全部 运行nable。

(defvar *word-dict* nil)

(defun set-isa (part-of-speech &rest words)
  (do ((wtail words))
      ((null wtail) nil) 
    (putp part-of-speech *word-dict* wtail)
    (setf wtail (cdr wtail))))

(defun putp (part-of-speech dict thing)
  (format *debug-io* "~&putp: ~A ~A ~A~%" part-of-speech dict thing))

所以这现在 运行 可用,putp 将打印它作为参数得到的内容。

 > (set-isa 'verb 'talk 'run 'jump )
putp: verb nil (talk run jump)
putp: verb nil (run jump)
putp: verb nil (jump)
nil

所以即使这里没有实际上有问题的代码,这几乎肯定是 putp,你也可以找出问题所在:putp replaces 存储在散列 table 中的任何值及其参数。所以最终在 table 中的唯一值是最后一个。所以我们需要解决这个问题,稍后我会做。

但事实上这并不是唯一的问题。

首先,您使用 do 的方式很奇怪。 do 的语法明确允许初始化 和步进 形式,因此您实际上应该使用步进形式而不是在正文中使用。

(defun set-isa (part-of-speech &rest words)
  (do ((wtail words (rest wtail)))
      ((null wtail) nil) 
    (putp part-of-speech *word-dict* wtail)))

其次,您在 列表的所有尾部 上调用 putp:您可能只想在单个词上调用它。您可以通过简单地将每条尾巴的汽车传递给它来做到这一点,但正如 Martin Buchmann 在另一个答案中指出的那样,您可能会寻找一种迭代列表元素的语言结构。还有很多,dolist 就是其中之一:

(defun set-isa (part-of-speech &rest words)
  (dolist (word words)
    (putp part-of-speech *word-dict* word)))

现在

(set-isa 'verb 'talk 'run 'jump )
putp: verb nil talk
putp: verb nil run
putp: verb nil jump
nil

注意 putp 的调用方式与以前的版本不兼容:现在是在单词上调用,而不是列表的尾部。

最后,让我们编写一个可以运行的 putp 版本。我会先写一个非常幼稚的版本:

(defvar *word-dict* (make-hash-table))

(defun putp (part-of-speech dict thing)
  (let ((entries (gethash part-of-speech dict '())))
    (setf (gethash part-of-speech dict) (cons thing entries))))

这行得通,但不是很好:

> (gethash 'verb *word-dict* '())
nil
nil

> (set-isa 'verb 'talk 'run 'jump )
nil

> (gethash 'verb *word-dict* '())
(jump run talk)
t

> (set-isa 'verb 'talk 'run 'jump )
nil

> (gethash 'verb *word-dict* '())
(jump run talk jump run talk)
t

那没关系,只要你只 运行 它一次。好吧,我们可以做得更好:

  • 我们可以使用更惯用的方式将新事物推送到存储在散列中的列表中table;
  • 我们可以避免重复输入的事情,同时也更加地道。

像这样:

(defun putp (part-of-speech dict thing)
  (pushnew thing (gethash part-of-speech dict)))

那么,现在:

 > (gethash 'verb *word-dict* '())
nil
nil

> (set-isa 'verb 'talk 'run 'jump )
nil

> (gethash 'verb *word-dict* '())
(jump run talk)
t

> (set-isa 'verb 'talk 'run 'jump )
nil

> (gethash 'verb *word-dict* '())
(jump run talk)

这样好多了。您可以查找 pushpushnew 以查看它们的作用。