src/wam/topological-sort.lisp @ fcec9e0c9c67
Fix the topological sorting
Necessary before we start working with program terms
author |
Steve Losh <steve@stevelosh.com> |
date |
Sat, 26 Mar 2016 19:19:07 +0000 |
parents |
(none) |
children |
d4ba6547d8a1 |
(in-package #:bones.wam)
;;;; Topological Sort
;;; Adapted from the AMOP book to add some flexibility (and remove the
;;; tie-breaker functionality, which we don't need).
(defun topological-sort
(elements constraints &key (key #'identity) (key-test #'eql) (test #'equal))
"Return a topologically sorted list of `elements` given the `constraints`.
`elements` should be a sequence of elements to be sorted.
`constraints` should be a list of `(element . element)` conses where
`(foo . bar)` means that element `foo` must precede `bar` in the result.
`key` will be used to turn items in `elements` into the items in
`constraints`.
`key-test` is the equality predicate for keys.
`test` is the equality predicate for (non-keyified) elements.
"
(labels
((find-minimal-elements (elements constraints)
;; An element is minimal if there are no other elements that are
;; required to precede it.
(remove-if #'(lambda (element)
(member (funcall key element)
constraints
:key #'cdr
:test key-test))
elements))
(in-constraint (val constraint)
;; Return whether val is either part of a constraint.
(or (funcall key-test val (car constraint))
(funcall key-test val (cdr constraint))))
(recur (remaining-constraints remaining-elements result)
(let ((minimal-elements (find-minimal-elements remaining-elements
remaining-constraints)))
(if (null minimal-elements)
(if (null remaining-elements)
result
(error "Inconsistent constraints."))
(let ((choice (car minimal-elements)))
(recur (remove (funcall key choice)
remaining-constraints
:test #'in-constraint)
(remove choice remaining-elements :test test)
(cons choice result)))))))
(reverse (recur constraints elements (list)))))