Graph Lisp-编写计算图形节点标签的函数

Graph Lisp-编写计算图形节点标签的函数,graph,lisp,common-lisp,labeling,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)

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

(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,更不用说如何对其进行编码了

因此,有两个问题:

  • 这个函数的“Lispy”伪代码是什么样子的
  • 你会在函数中加入哪些具体的语法特征?让我们假设有一个
    条件
    每个
    对这个问题有用吗?我们可以不借助lambda表达式轻松地做到这一点吗

  • 提前感谢您的反馈

    好的编程的一个重要方面,不管是哪种语言,都是好的抽象。有时,这可能是一个品味的问题,但这里有一个例子,试图应用一些抽象来解决这个问题。一旦有了图形和值,就可以定义一个返回节点值的节点值函数。然后你可以把你的问题表述为

    图中是否存在与其相邻节点具有相同节点值的节点

    一些来写这并不难:

    (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)))
    
    (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))))))
    

    这就是说,尽管在某些意义上这可能更为Lisp-y,但使用显式迭代与dolist编写它也同样有意义:

    (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)))
    
    (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)))
        ;; ...
        ))  
    

    好的编程的一个重要方面,不管是哪种语言,都是好的抽象。有时,这可能是一个品味的问题,但这里有一个例子,试图应用一些抽象来解决这个问题。一旦有了图形和值,就可以定义一个返回节点值的节点值函数。然后你可以把你的问题表述为

    图中是否存在与其相邻节点具有相同节点值的节点

    一些来写这并不难:

    (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)))
    
    (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))))))
    

    这就是说,尽管在某些意义上这可能更为Lisp-y,但使用显式迭代与dolist编写它也同样有意义:

    (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)))
    
    (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)))
        ;; ...
        ))  
    

    好的编程的一个重要方面,不管是哪种语言,都是好的抽象。有时,这可能是一个品味的问题,但这里有一个例子,试图应用一些抽象来解决这个问题。一旦有了图形和值,就可以定义一个返回节点值的节点值函数。然后你可以把你的问题表述为

    图中是否存在与其相邻节点具有相同节点值的节点

    一些来写这并不难:

    (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)))
    
    (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))))))
    

    这就是说,尽管在某些意义上这可能更为Lisp-y,但使用显式迭代与dolist编写它也同样有意义:

    (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)))
    
    (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)))
        ;; ...
        ))  
    

    好的编程的一个重要方面,不管是哪种语言,都是好的抽象。有时,这可能是一个品味的问题,但这里有一个例子,试图应用一些抽象来解决这个问题。一旦有了图形和值,就可以定义一个返回节点值的节点值函数。然后你可以把你的问题表述为

    图中是否存在与其相邻节点具有相同节点值的节点

    一些来写这并不难:

    (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)))
    
    (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))))))
    

    也就是说,尽管从某种意义上说,这可能更容易口齿不清