Я пытаюсь написать функцию, которая принимает список определяемых пользователем объектов с именем nodes
для создания связей между ними. Каждый объект node
имеет слот для своего уникального номера ('num') и слот для списка чисел, которые выступают в качестве ребер между узлами («ребра»). +max-edges+
- это целое число, которое определяет, сколько раз будет предпринято парное сопряжение, а +max-rooms+
- количество узлов в списке узлов, передаваемых в функцию (и всегда < 50).Общая функция функции setstring объекта Lisp
Вот две версии функции, которая пытается решить эту проблему:
(defun connect-nodes (node-list)
"Given a NODE-LIST, repeats for +MAX-EDGES+ amount of times
to alter NODE-LIST in-place to connect randomly generated edges to nodes."
(loop repeat +max-edges+
do (let ((begin-node (random +max-rooms+))
(end-node (random +max-rooms+)))
(when (not (= begin-node end-node))
(setf (slot-value (nth begin-node node-list) 'edges)
(cons end-node
(slot-value (nth begin-node node-list) 'edges)))
(setf (slot-value (nth end-node node-list) 'edges)
(cons begin-node
(slot-value (nth end-node node-list) 'edges))))))))
(defun connect-nodes% (node-list)
"Given a NODE-LIST, repeats for +MAX-EDGES+ amount of times
to alter NODE-LIST in-place to connect randomly generated edges to nodes."
(loop repeat +max-edges+
do (let ((begin-node (random +max-rooms+))
(end-node (random +max-rooms+)))
(when (not (= begin-node end-node))
(let ((begin-node-lst (slot-value (nth begin-node node-list) 'edges))
(end-node-lst (slot-value (nth end-node node-list) 'edges)))
(setf begin-node-lst (cons end-node begin-node-lst))
(setf end-node-lst (cons begin-node end-node-lst)))))))
(connect-nodes)
работает, как ожидалось, но последние две строки кажутся стилистически долго и поиска значения слота для объекта setf
«D дважды, что, я думаю, может быть проблемой производительности.
(connect-nodes%)
пытается решить двойной поиск путем привязки местоположения в лексическом пространстве, но на самом деле не изменяет аргумент списка узлов на месте. Изменения не производятся, поскольку каждое место в привязке let
(begin-node-lst
и end-node-lst
) является обязательным только лексически и выходит за рамки после setf
s.
Поэтому я прошу разъяснить несколько пунктов:
- ли мое понимание того, почему вторая функция не может изменить список аргументов правильно?
- Является ли первая функция стилистически правильной? Есть ли лучший способ написать эту функцию, которая не ищет значение слота дважды для
setf
или это приемлемо для небольших списков длины?
Я запускаю slime + emacs + sbcl, если это влияет на ваш ответ.
EDIT: Вот что я в конечном итоге происходит с для списка-версии connect-nodes
функции благодаря советам из ответов на мой вопрос. Я работаю над версией, которая работает над векторами, следовательно, эта версия connect-nodes
является метод на родовой функции:
(defmethod connect-nodes ((node-list list))
"Given a NODE-LIST, repeats for +MAX-EDGES+ amount of times
to alter NODE-LIST in-place to connect randomly generated edges to nodes."
(loop repeat +max-edges+
do (let ((begin-node (random +max-rooms+))
(end-node (random +max-rooms+)))
(when (not (= begin-node end-node))
(push end-node (edges (nth begin-node node-list)))
(push begin-node (edges (nth end-node node-list)))))))
Общий совет я получаю: 1) использовать макрос 'PUSH', который выполняет шаблон' SETF', определяющий значение элемента, связанного с списком в месте. 2) Кажется, расточительно хранить цифровые ключи поиска для моих объектов, когда я мог просто добавить ссылки на них непосредственно в слоте кромок. – qmoog