(module sets mzscheme (provide set set+ set- set-intersection set-union set-difference set-size empty-set? set->list set-exists? set-filter set-for-each set?) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head1 Sets ;;; ;;;This module provides a thread safe sets implementation. It is build ;;;upon L<AVL Trees|scheme datastructures - AVL Tree>. ;;; ;;;=verbatim scm,8 (require "avl.scm") ;;;=verbatim ;;; ;;;Being build upon AVL trees, this implementation requires ;;;both an 'is-equal?' and an 'is-less?' function to be able ;;;to work. This is a requirement that is normally quite easily ;;;fullfilled. ;;; ;;;=head2 On thread safety ;;; ;;;Although the basic operations on sets are thread safe, ;;;combining multiple sets into one operation, e.g. ;;;a set-intersection, will not guarantee thread safety for ;;;all sets. ;;; ;;;In general, thread safety of sets builds on the thread safety of the ;;;avl trees. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head2 Basic set functions ;;; ;;;=head3 C<(set is-equal? is-less?) : new set> ;;; ;;;Creates a new empty set. ;;; ;;;=verbatim scm,8 (define-struct %set (avl)) (define (set is-equal? is-less?) (make-%set (avl is-equal? is-less?))) ;;;=verbatim ;;; ;;;=head3 C<(set-from-set S:set) : new set> ;;; ;;;Creates a new empty set, using the is-equal? and is-less? functions ;;;from S. ;;; ;;;=verbatim scm,8 (define (set-from-set S) (make-%set (avl-from-avl (%set-avl S)))) ;;;=verbatim ;;; ;;;=head3 C<(set? obj) : boolean> ;;; ;;;Returns #t, if obj is a set, #f otherwise. ;;;Note: list? is also #t for a set. This means, ;;;one will normally first check if an object has predicate set?, ;;;after which a list? check is done. ;;; ;;;=verbatim scm,8 (define (set? obj) (%set? obj)) ;;;=verbatim ;;; ;;;=head3 C<(set-size S:set) : number> ;;; ;;;Returns the number of objects in a set. ;;; ;;;=verbatim scm,8 (define (set-size set) (avl-nodes (%set-avl set))) ;;;=verbatim ;;; ;;;=head3 C<(emtpy-set? S:set) : boolean> ;;; ;;;Returns #t, is S is empty; #f otherwise. ;;; ;;;=verbatim scm,8 (define (empty-set? S) (= (set-size S) 0)) ;;;=verbatim ;;; ;;;=head3 C<(set+ S:set obj) : set (=S)> ;;; ;;;Adds obj to S, unless obj is already present in S. ;;;Returns S. ;;; ;;;=verbatim scm,8 (define (set+ set obj) (let ((found #t)) (avl-find (%set-avl set) obj (lambda () (set! found #f))) (if (not found) (avl-insert! (%set-avl set) obj)) set)) ;;;=verbatim ;;; ;;;=head3 C<(set- S:set obj) : set (=S)> ;;; ;;;Removes obj from S, if obj is part of S. ;;;Returns S. ;;; ;;;=verbatim scm,8 (define (set- set obj) (begin (avl-remove! (%set-avl set) obj) set)) ;;;=verbatim ;;; ;;;=head3 C<(set-E<gt>list? S:set) : list> ;;; ;;;Converts a set to a list. All elements of ;;;the set are in the list. Although the returned ;;;list seems ordered, sets are by definition unordered. ;;;The ordering of the list cannot be relied on. ;;; ;;;=verbatim scm,8 (define (set->list set) (let ((L '())) (avl-for-each (%set-avl set) (lambda (obj level) (set! L (cons obj L)))) L)) ;;;=verbatim ;;; ;;;=head3 C<(set-filter S:set function F:boolean) : set> ;;; ;;;Calls function F for all elements of S. Each element ;;;of S for which F returns #t will be part of the result ;;;set. ;;; ;;;=verbatim scm,8 (define (set-filter S F) (let ((R (set-from-set S))) (avl-for-each (%set-avl S) (lambda (obj level) (if (F obj) (set+ R obj)))) R)) ;;;=verbatim ;;; ;;;=head3 C<(set-for-each S:set function F:boolean) : S> ;;; ;;;Calls function F for all elements of S. Result set = S ;;; ;;;=verbatim scm,8 (define (set-for-each S F) (avl-for-each (%set-avl S) (lambda (obj level) (F obj))) S) ;;;=verbatim ;;; ;;;=head3 C<(set-exists? S:set obj) : boolean> ;;; ;;;Returns #t, if obj exists in S; #f otherwise. ;;; ;;;=verbatim scm,8 (define (set-exists? set obj) (avl-exists? (%set-avl set) obj)) ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head2 Set operations ;;; ;;;=head3 Supportive macro to define set operations ;;; ;;;This macro provides (naive) generic code for standard set ;;;operations. It is not used for the intersection operation, ;;;which has been optimized. ;;; ;;;=verbatim scm,8 (define-syntax worker (syntax-rules () ((_ function function1 function1-definition) (define (function set1 . sets) function1-definition (define (%worker set1 sets) (if (null? sets) set1 (%worker (function1 set1 (car sets)) (cdr sets)))) (%worker set1 sets))))) ;;;=verbatim ;;; ;;;=head3 C<(set-intersection . S:list of 1 or more objects with predicate set?) : new set> ;;; ;;;This function calculates the intersection of all ;;;given sets in S. Calculating the intersection is ;;;of O(#S*n*log(n)) time, where n is the size of the ;;;first set. This function looks for the smallest ;;;set to begin with, to get n as small as possible. ;;; ;;;This function is not thread safe for all ;;;given sets. Concurrent updating of sets during ;;;this operation will give unexpected results. ;;; ;;;Returns a new set that is the intersection of all ;;;given sets. ;;; ;;;=verbatim scm,8 (define (set-intersection . sets) (define (set-intersect1 set1 set2) (define (intersect-node avl2 obj) (let ((found #t)) (avl-find avl2 obj (lambda () (set! found #f))) found)) (let ((avl1 (%set-avl set1)) (avl2 (%set-avl set2))) (let ((result-set (make-%set (avl-filter avl1 (lambda (obj level) (intersect-node avl2 obj)))))) result-set))) (define (intersection set1 sets) (define (%worker set1 sets) (if (null? sets) set1 (%worker (set-intersect1 set1 (car sets)) (cdr sets)))) (%worker set1 sets)) (define (find-minimum-set-size minimum sets) (if (null? sets) minimum (if (< (set-size (car sets)) (set-size minimum)) (find-minimum-set-size (car sets) (cdr sets)) (find-minimum-set-size minimum (cdr sets))))) (define (filter-out-minimum minimum sets) (if (null? sets) (list) (if (eq? (car sets) minimum) (filter-out-minimum minimum (cdr sets)) (cons (car sets) (filter-out-minimum minimum (cdr sets)))))) (if (null? sets) (error "set-intersection: I need at least 1 set") (let ((set-with-minimum-size (find-minimum-set-size (car sets) (cdr sets)))) (intersection set-with-minimum-size (filter-out-minimum set-with-minimum-size sets))))) ;;;=verbatim ;;; ;;;=head3 C<(set-union set1:set . S:list of 0 or more objects with predicate set?) : new set> ;;; ;;;This function returns a new set, which is the union ;;;of all given sets. ;;; ;;;=verbatim scm,8 (worker set-union set-unite1 (define (set-unite1 set1 set2) (let ((result-set (make-%set (avl-map (%set-avl set1) (lambda (obj level) obj))))) (avl-for-each (%set-avl set2) (lambda (obj level) (set+ result-set obj))) result-set))) ;;;=verbatim ;;; ;;;=head3 C<(set-difference set1:set . S:list of 0 or more objects with predicate set?) : new set> ;;; ;;;This function calculates the difference between set1 and the car of S. ;;;After that, the difference between the resulting set and the cadr of S ;;;is calculated and so on. The last resulting set is returned. ;;; ;;;=verbatim scm,8 (worker set-difference set-subtract1 (define (set-subtract1 set1 set2) (define (subtract-node avl2 obj) (let ((found #t)) (avl-find avl2 obj (lambda () (set! found #f))) (not found))) (let ((avl1 (%set-avl set1)) (avl2 (%set-avl set2))) (let ((result-set (make-%set (avl-filter avl1 (lambda (obj level) (subtract-node avl2 obj)))))) result-set)))) ;;;=verbatim ;;; ;;;=head2 Info ;;; ;;;S<C<Author(s):>> Hans Oesterholt (hansatelementalprogrammingdotorgextension).E<lb> ;;;S<C<Copyright:>> (c) 2005.E<lb> ;;;S<C<License :>> L<Elemental Programming License|http://www.elemental-programming.org/epwiki/ep_license.html>.E<lb> ;;;S<C<File :>> sets.scm $Revision: 1.2 $ ;;; ;;;=cut )