Lisp - 编写一个评估图节点标签的函数

时间:2014-09-28 21:39:55

标签: graph lisp common-lisp labeling

考虑一个由节点和邻居组成的图表:

(defparameter *graph* '((A (B C D))
                        (B (A C E))
                        (C (A B D E))
                        (D (A C E))
                        (E (B C D))))

...以及每个节点的一组标签:

(defparameter *values* '((A 1)
                         (B 2)
                         (C 3)
                         (D 2)
                         (E 1)))

我正在尝试编写一个函数来评估该格式的图形,并确定相邻节点是否具有相同的标签。如果我用C ++或Java编写这个函数,那么函数的迭代版本的逻辑可能如下所示:

(defun evaluate-label (graph values)
;; for every node in graph
  ;; for every adjoining node
    ;; if (node.value == adjoiningNode.value)
      ;; return false
;; return true
)

...但我不确定哪种逻辑更适合Lisp,更不用说如何编写代码了。

所以,有两个问题:

  1. 这个函数的“Lispy”位伪代码是什么样的?
  2. 您将在函数中添加哪些特定的语法功能?我们假设有一个condevery对此问题有用吗?我们可以轻松地执行此操作而无需使用lambda表达式吗?
  3. 提前感谢您的任何反馈!

1 个答案:

答案 0 :(得分:3)

良好的编程的一个重要方面,无论语言如何,都是良好的抽象。有时,这可能是一个品味问题,但这里是一个试图对这个问题应用一些抽象的例子。获得图形和值后,可以定义一个返回节点值的节点值函数。然后你可以将你的问题短语

  

图表中是否存在与其邻居之一具有相同节点值的节点?

使用某些

写这个并不难
(defun adjacent-values-p (graph values)
  (flet ((node-value (node)
           (cadr (assoc node values))))
    (some #'(lambda (node-descriptor)
              (destructuring-bind (node neighbors)
                  node-descriptor
                (find (node-value node) neighbors
                      :key #'node-value)))
          graph)))

(adjacent-values-p '((a (b c)))
                   '((a 1) (b 2) (c 1)))
;=> C

(adjacent-values-p '((a (b c)))
                   '((a 1) (b 2) (c 3)))
;=> NIL

尽管如此,即使在某种意义上可能更多的是Lisp-y,但使用 dolist 进行显式迭代来编写它可能同样有意义:

(defun adjacent-values-p (graph values)
  (flet ((node-value (node)
           (cadr (assoc node values))))
    (dolist (node-descriptor graph)
      (destructuring-bind (node neighbors) node-descriptor
        (when (member (node-value node) neighbors :key #'node-value)
          (return t))))))

这可以通过循环更好,它支持一些解构:

(defun adjacent-values-p (graph values)
  (flet ((node-value (node)
           (cadr (assoc node values))))
    (loop for (node neighbors) in graph
       thereis (find (node-value node) neighbors :key #'node-value))))

所有这些版本都可以从例如存储值中受益。用于更快检索的哈希表。这是否有意义取决于您的需求,应用程序域等。否则您将检索边标签O(2×| E |),每次执行O(| V |)遍历。例如:

(let ((table (make-hash-table)))
  (flet ((node-value (node)
           (multiple-value-bind (value presentp)
               (gethash node table)
             (if presentp value
                 (setf (gethash node table)
                       (cadr (assoc node values)))))))
    ;; ...
    ))

通过在需要之前不查找节点值来缓存“按需”。但是,由于应该需要每个节点值(假设提供的值列表不包含任何额外节点),最好只在开头填充表。然后,您不必在以后进行任何检查,只需遍历值列表一次。因此:

(defun adjacent-values-p (graph values &aux (table (make-hash-table)))
  (loop for (node value) in values
     doing (setf (gethash node table) value))
  (flet ((node-value (node)
           (gethash node table)))
    ;; ...
    ))