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