Итак, способ понять, что здесь происходит, состоит в том, чтобы аннотировать ваш код, чтобы он сообщал вам, что он делает . Это может показаться античным способом отладки, но в динамическом c, разговорном языке, таком как CL, это действительно хороший подход. Вот версия вашей функции, использующая более общепринятые имена для вещей, а также использующая обычную неопределенность вместе с заглушками для отсутствующего кода, чтобы сделать его работоспособным.
(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
заменяет любое значение, хранящееся в таблице ha sh, своим аргументом. Таким образом, единственное значение, которое заканчивается в таблице, является последним. Поэтому нам нужно исправить это, что я и сделаю позже.
Но на самом деле это не единственная проблема.
Прежде всего, вы используете 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
на все хвосты вашего списка : вы, вероятно, хотите назвать его только отдельными словами. Вы можете сделать это, просто передав ему машину каждого хвоста, но, как указывает Мартин Бухманн в другом ответе, вы можете вместо этого искать в языке конструкцию, которая перебирает элементы списка. И их много, и 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
Так что все в порядке, пока вы только запускаете это один раз. Что ж, мы можем сделать лучше, чем это:
- мы можем использовать более идиоматический c способ добавления новых вещей в список, хранящийся в хеш-таблице;
- мы можем избежать дублирующая запись, в то же время будучи более идиоматическим c.
Примерно так:
(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)
Это намного лучше , Вы можете посмотреть push
и pushnew
, чтобы увидеть, что они делают.