;
;; First, a few lines to make this file usable as a teachpack ;; (For details, see htus.org ("how to use scheme"), or ;; http://www.owlnet.rice.edu/~comp210/02fall/Handouts/teachpack-demo.shtml ) ;; (module set (lib "plt-pretty-big.ss" "lang") (provide element-of? ; any, Set --> boolean intersect ; Set, Set --> Set union ; Set, Set --> Set cars ; Set (test case) reals<10 ; Set (test case) reals>=4 ; Set (test case) ) ;; We base our data-def'n of a set on the ways ;; mathematicians write sets. ;; We call it "inft-Set" since it can represent infinite sets. ; ;; A inft-Set of alpha is either: ;; - a list of alpha, or ;; - alpha --> boolean (the set's "indicator" function) ; Some examples of the data: ; (define cars (list 'vw 'bmw 'audi 'saab 'geo)) (define (reals<10 x) (and (real? x) (< x 10))) (define (reals>=4 x) (and (real? x) (>= x 4))) ; Observation: ; The list representation can only be used for finite sets. ; The indicator-function representation can accomodate finite or infinite sets. ;; element-of?: ANY, inft-Set --> boolean ;; Is x in S? ;; ;; (Implementation note: ;; compares via 'equal?' (not 'eq?') ;; if given a list-representation of a Set/infinite.) ;; ; ;; (define (element-of? x S) (cond [(list? S) (not (boolean? (member x S)))] [(procedure? S) (S x)])) ; ; Not an issue unless reference-values are used: ; If we wanted to use eq? rather than equal?, ; we'd use "memq" instead of "member". ; Either way, the indicator function implementation doesn't ; have this ambiguity -- the function itself is given the ; input (possibly a reference), and decides the answer for itself. ; Tests: ; (element-of? 'vw cars) = true (element-of? 'yugo cars) = false (element-of? 4 reals<10) = true (element-of? 13 reals<10) = false (element-of? 'vw reals<10) = false ;; intersect: Set, Set --> Set ;; union: Set, Set --> Set ;; ;; Implementation note: the returned Set happens to ;; be represented as an indicator function, regardless of the input ;; representation. ;; (Not that the user of the abstract type "Set" cares.) ;; ; ;; (define (intersect A B) (lambda (elt) (and (element-of? elt A) (element-of? elt B)))) (define (union A B) (lambda (elt) (or (element-of? elt A) (element-of? elt B)))) ; Tests: ; (element-of? 'vw (union cars reals<10)) = true (element-of? 9 (union cars reals<10)) = true (element-of? 11 (union cars reals<10)) = false (element-of? 11 (union reals>=4 reals<10)) = true (element-of? 3 (union reals>=4 reals<10)) = true (element-of? 6 (union reals>=4 reals<10)) = true (element-of? 'yugo (intersect cars reals<10)) = false (element-of? 'vw (intersect cars reals<10)) = false (element-of? 6 (intersect reals>=4 reals<10)) = true (element-of? 11 (intersect reals>=4 reals<10)) = false (element-of? 3 (intersect reals>=4 reals<10)) = false ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Doomed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; An attempt, which will be scotched by not being ;; able to implement subset? for inft-sets. ;; (This turns out to be a fundamental limit on computation -- not ;; an artifact of our language, representation, etc.) ;; However, for finite sets, this code works like a charm. ; ;; ;; inft-set=?: set, set --> boolean ;; (define (inft-set=? A B) (and (subset? A B) (subset? B A))) ; ; This code directly parallels our (alternate) def'n of equal sets ; -- happiness! ;; empty-set?: set --> boolean ;; (define (empty-set? A) (inft-set=? empty A)) ; ; Pointed out: Could also write (subset? A empty). ; Empty is one possible representation of the empty set; what is another? ;; subset?: set, set --> boolean ;; (define (subset? A B) (error 'subset? "Fundamentally can't implement subset? for infinite sets.")) ; ; If we restrict ourselves to finite sets, then we could write subset?. ; We still would not want to change the code for set=? or empty-set? -- ; The compiler can do any optimization; we'll keep our code clearly ; reflecting our definitions. ; ; See the reading (Rosen 1.7) for implementations of finite sets. #| ;;;;;;;;;;;;;;;;; Aside: An object hierarchy for finite & infinite sets. In a class system, how should classes inft-set and finite-set be arranged? ; (define-class Inft-set (extends Object) (public* element-of?) (static* union intersect)) ; Make these 2-arg functions static, not methods, ; since neither of the two args have more importance than the other. (define-class Finite-set (extends Inft-set) (inherit* element-of? union intersect) (public* subset? empty-set?) (static* set=?) ; Make this 2-arg functions static, not methods, ; since neither of the two args have more importance than the other. ) (N.B. This isn't exactly drscheme's object syntax; see help-desk for the official details.) It's a bit weird english, to say "Finite-set extends Inft-set"; clearly finite sets are a >restriction< of infinite sets. However, in terms of behavior, we are extending the number of methods. Indeed, the is-a relation certainly holds between these classes: every Finite-set is-a Inft-set. (Okay, maybe a better name would have been "Possibly-infinite-set", oh well.) This is analagous to "class Cat extends Animal" -- Cats are a restricted type of Animal, but they have more behaviors than generic Animals, such as shredOwnersOttoman! (a function with side-effects). One annoying thing: if we wanted, for Finite-sets, versions of union and intersect that returned *finite* sets (rather than just Sets), we'd just override those methods. HOWEVER, most languages don't allow modifying the signature of the inherited function. This is true even though: - we want to express that Finite-set.union() *must* always return a Finite-set, and - we are still meeting the original contract, since every Finite-set is-a Inft-set. This weakness of the type-system keeps us from properly expressing the typing that we really want to enforce (and, may force us to add type-casts to our code later -- ugly!). |# ) ; End module. ;