~ chicken-core (chicken-5) 9247cec3930849e6570a18333739b9733daed2ce
commit 9247cec3930849e6570a18333739b9733daed2ce Author: Moritz Heidkamp <moritz@twoticketsplease.de> AuthorDate: Sun Mar 3 13:28:20 2013 +0100 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Fri May 10 10:41:48 2013 +0200 Reimplement topological-sort with cycle detection. Signed-off-by: felix <felix@call-with-current-continuation.org> diff --git a/data-structures.scm b/data-structures.scm index 56944ec0..54089249 100644 --- a/data-structures.scm +++ b/data-structures.scm @@ -707,52 +707,49 @@ (sort! (append seq '()) less?))) -;;; Simple topological sort: -; -; Taken from SLIB (slightly adapted): Copyright (C) 1995 Mikael Djurfeldt +;;; Topological sort with cycle detection: +;; +;; A functional implementation of the algorithm described in Cormen, +;; et al. (2009), Introduction to Algorithms (3rd ed.), pp. 612-615. (define (topological-sort dag pred) - (if (null? dag) - '() - (let* ((adj-table '()) - (sorted '())) - - (define (insert x y) - (let loop ([at adj-table]) - (cond [(null? at) (set! adj-table (cons (cons x y) adj-table))] - [(pred x (caar at)) (set-cdr! (car at) y)] - [else (loop (cdr at))] ) ) ) - - (define (lookup x) - (let loop ([at adj-table]) - (cond [(null? at) #f] - [(pred x (caar at)) (cdar at)] - [else (loop (cdr at))] ) ) ) - - (define (visit u adj-list) - ;; Color vertex u - (insert u 'colored) - ;; Visit uncolored vertices which u connects to - (for-each (lambda (v) - (let ((val (lookup v))) - (if (not (eq? val 'colored)) - (visit v (or val '()))))) - adj-list) - ;; Since all vertices downstream u are visited - ;; by now, we can safely put u on the output list - (set! sorted (cons u sorted)) ) - - ;; Hash adjacency lists - (for-each (lambda (def) (insert (car def) (cdr def))) - (cdr dag)) - ;; Visit vertices - (visit (caar dag) (cdar dag)) - (for-each (lambda (def) - (let ((val (lookup (car def)))) - (if (not (eq? val 'colored)) - (visit (car def) (cdr def))))) - (cdr dag)) - sorted) ) ) + (define (visit dag node edges path state) + (case (alist-ref node (car state) pred) + ((grey) + (##sys#abort + (##sys#make-structure + 'condition + '(exn runtime cycle) + `((exn . message) "cycle detected" + (exn . arguments) ,(list (cons node (reverse path))) + (exn . call-chain) ,(##sys#get-call-chain) + (exn . location) topological-sort)))) + ((black) + state) + (else + (let walk ((edges (or edges (alist-ref node dag pred '()))) + (state (cons (cons (cons node 'grey) (car state)) + (cdr state)))) + (if (null? edges) + (cons (alist-update! node 'black (car state) pred) + (cons node (cdr state))) + (let ((edge (car edges))) + (walk (cdr edges) + (visit dag + edge + #f + (cons edge path) + state)))))))) + (let loop ((dag dag) + (state (cons (list) (list)))) + (if (null? dag) + (cdr state) + (loop (cdr dag) + (visit dag + (caar dag) + (cdar dag) + '() + state))))) ;;; Binary search: diff --git a/manual/Unit data-structures b/manual/Unit data-structures index 5412ea4d..c64432e1 100644 --- a/manual/Unit data-structures +++ b/manual/Unit data-structures @@ -282,6 +282,9 @@ Time complexity: O (|V| + |E|) (socks undershorts pants shoes watch shirt belt tie jacket) </enscript> +If a cycle is detected during the sorting process, an exception of the +condition kinds {{(exn runtime cycle)}} is thrown. + === Random numbers diff --git a/tests/data-structures-tests.scm b/tests/data-structures-tests.scm index b0f8794b..de3872b6 100644 --- a/tests/data-structures-tests.scm +++ b/tests/data-structures-tests.scm @@ -2,6 +2,11 @@ (use data-structures) +(define-syntax assert-error + (syntax-rules () + ((_ expr) + (assert (handle-exceptions _ #t expr #f))))) + (let ((alist '((foo . 123) ("bar" . "baz")))) (alist-update! 'foo 999 alist) (assert (= (alist-ref 'foo alist) 999)) @@ -37,3 +42,10 @@ (assert (> 0 (string-compare3-ci "foo\x00A" "foo\x00b"))) (assert (< 0 (string-compare3-ci "foo\x00b" "foo\x00a"))) (assert (< 0 (string-compare3-ci "foo\x00b" "foo\x00A"))) + +;; topological-sort + +(assert (equal? '() (topological-sort '() eq?))) +(assert (equal? '(a b c d) (topological-sort '((a b) (b c) (c d)) eq?))) +(assert (equal? '(c d a b) (topological-sort '((a b) (c d)) eq?))) +(assert-error (topological-sort '((a b) (b a)) eq?))Trap