与其痴迷于实现,我会设计一个系统需要遵循的协议。这是一个这样的(注意我在这里做了一些假设,其中一些可能是隐含的,并且没有一个可能与您希望事情的工作方式一致):
;;;; Protocol
;;;
;;; By assumption there is one link with each label, each link points
;;; at one other node.
;;;
;;; NODEs have identity and can be destructively modified but it is
;;; not specified whether node equality is object identity.
;;;
(defgeneric node-link-labelled (node label)
(:documentation "Return the node linked to NODE via LABEL, or NIL".))
(defgeneric (setf node-link-labelled) (target node label)
(:documentation "set the link with label LABEL of NODE to TARGET, replacing it if
it exists. Return TARGET."))
(defgeneric nodes-equal (n1 n2)
(:documentation "Are N1 and N2 the same node?"))
(defgeneric node-remove-link (node label)
(:documentation "Remove the link with label LABEL from NODE. Return NODE.
The link need not exist"))
(defgeneric mapc-node-links (fn node)
(:documentation "call FN with arguments NODE, LABEL TARGET for each link of NODE.
FN is allowed to delete the link corresponding to LABEL but should not otherwise
modify NODE"))
然后你可以为这个协议编写实现。这是一个简单的节点是(<something> . <links>) 的conses。对于大量链接,这将很慢,但对于少量链接可能非常快。它有一个很好的特性,你可以给节点命名,这在上面的协议中是不支持的。
;;;; Consy nodes are simple
;;;
(defun make-consy-node (&optional (label 'node))
(list label))
(defmethod node-link-labelled ((node cons) label)
(cdr (assoc label (cdr node))))
(defmethod nodes-equal ((n1 cons) (n2 cons))
(eql n1 n2))
(defmethod (setf node-link-labelled) (target (node cons) label)
(let ((found (assoc label (cdr node))))
(if found
(setf (cdr found) target)
(push (cons label target) (cdr node))))
target)
(defmethod node-remove-link ((node cons) label)
(setf (cdr node) (delete-if (lambda (link)
(eql (car link) label))
(cdr node)))
node)
(defmethod mapc-node-links (fn (node cons))
;; This is at least safe
(loop for (label . target) in (copy-list (cdr node))
do (funcall fn node label target))
node)
或者您可以将节点实现为哈希表,这对于具有许多每个节点链接的图来说会很快:
;;;; Hashy nodes
;;;
(defun make-hashy-node ()
(make-hash-table))
(defmethod nodes-equal ((n1 hash-table) (n2 hash-table))
(eql n1 n2))
(defmethod node-link-labelled ((node hash-table) label)
(values (gethash label node nil)))
(defmethod (setf node-link-labelled) (target (node hash-table) label)
(setf (gethash label node) target)
target)
(defmethod node-remove-link ((node hash-table) label)
(remhash label node)
node)
(defmethod mapc-node-links (fn (node hash-table))
(maphash (lambda (label target)
(funcall fn node label target))
node)
node)
或者你可以做很多其他的事情。由于它们都遵循协议,因此您可以混合使用它们:
(let ((n1 (make-hashy-node)))
(setf (node-link-labelled n1 'foo) (make-hashy-node)
(node-link-labelled n1 'bar) (make-consy-node 'n2))
n1)
如果需要,您可以将节点构造定义为协议的一部分:
(defgeneric make-node-of-sort (sort &key)
(:documentation "make a node whose sort is SORT. Methods on this GF should
use EQL specializers on SORT"))
...
(defmethod make-node-of-sort ((sort (eql 'consy)) &key (name 'node))
(list name))
...