4.16 Sets
A set represents a collection of distinct elements. The following datatypes are all sets:
structures whose types implement the gen:set generic interface.
(require racket/set) | package: base |
4.16.1 Hash Sets
A hash set is a set whose elements are compared via equal?, eqv?, or eq? and partitioned via equal-hash-code, eqv-hash-code, or eq-hash-code. A hash set is either immutable or mutable; mutable hash sets retain their elements either strongly or weakly.
Like operations on immutable hash tables, “constant time” hash set operations actually require O(log N) time for a set of size N.
A hash set can be used as a stream (see Streams) and thus as a single-valued sequence (see Sequences). The elements of the set serve as elements of the stream or sequence. If an element is added to or removed from the hash set during iteration, then an iteration step may fail with exn:fail:contract, or the iteration may skip or duplicate elements. See also in-set.
Two hash sets are equal? when they use the same element-comparison procedure (equal?, eqv?, or eq?), both hold elements strongly or weakly, have the same mutability, and have equivalent elements. Immutable hash sets support effectively constant-time access and update, just like mutable hash sets; the constant on immutable operations is usually larger, but the functional nature of immutable hash sets can pay off in certain algorithms.
All hash sets implement set->stream, set-empty?, set-member?, set-count, subset?, proper-subset?, set-map, set-for-each, set-copy, set-copy-clear, set->list, and set-first. Immutable hash sets in addition implement set-add, set-remove, set-clear, set-union, set-intersect, set-subtract, and set-symmetric-difference. Mutable hash sets in addition implement set-add!, set-remove!, set-clear!, set-union!, set-intersect!, set-subtract!, and set-symmetric-difference!.
Operations on sets that contain elements that are mutated are unpredictable in much the same way that hash table operations are unpredictable when keys are mutated.
procedure
(set-equal? x) → boolean?
x : any/c
procedure
x : any/c
procedure
x : any/c
procedure
x : any/c
procedure
(set-mutable? x) → boolean?
x : any/c
procedure
x : any/c
procedure
(set v ...) → (and/c generic-set? set-equal? set?)
v : any/c
procedure
(seteqv v ...) → (and/c generic-set? set-eqv? set?)
v : any/c
procedure
(seteq v ...) → (and/c generic-set? set-eq? set?)
v : any/c
procedure
(mutable-set v ...)
→ (and/c generic-set? set-equal? set-mutable?) v : any/c
procedure
(mutable-seteqv v ...)
→ (and/c generic-set? set-eqv? set-mutable?) v : any/c
procedure
(mutable-seteq v ...)
→ (and/c generic-set? set-eq? set-mutable?) v : any/c
procedure
(weak-set v ...) → (and/c generic-set? set-equal? set-weak?)
v : any/c
procedure
(weak-seteqv v ...) → (and/c generic-set? set-eqv? set-weak?)
v : any/c
procedure
(weak-seteq v ...) → (and/c generic-set? set-eq? set-weak?)
v : any/c
procedure
(list->set lst) → (and/c generic-set? set-equal? set?)
lst : list?
procedure
(list->seteqv lst) → (and/c generic-set? set-eqv? set?)
lst : list?
procedure
(list->seteq lst) → (and/c generic-set? set-eq? set?)
lst : list?
procedure
(list->mutable-set lst)
→ (and/c generic-set? set-equal? set-mutable?) lst : list?
procedure
(list->mutable-seteqv lst)
→ (and/c generic-set? set-eqv? set-mutable?) lst : list?
procedure
(list->mutable-seteq lst)
→ (and/c generic-set? set-eq? set-mutable?) lst : list?
procedure
(list->weak-set lst)
→ (and/c generic-set? set-equal? set-weak?) lst : list?
procedure
(list->weak-seteqv lst)
→ (and/c generic-set? set-eqv? set-weak?) lst : list?
procedure
(list->weak-seteq lst) → (and/c generic-set? set-eq? set-weak?)
lst : list?
syntax
(for/set (for-clause ...) body ...+)
syntax
(for/seteq (for-clause ...) body ...+)
syntax
(for/seteqv (for-clause ...) body ...+)
syntax
(for*/set (for-clause ...) body ...+)
syntax
(for*/seteq (for-clause ...) body ...+)
syntax
(for*/seteqv (for-clause ...) body ...+)
syntax
(for/mutable-set (for-clause ...) body ...+)
syntax
(for/mutable-seteq (for-clause ...) body ...+)
syntax
(for/mutable-seteqv (for-clause ...) body ...+)
syntax
(for*/mutable-set (for-clause ...) body ...+)
syntax
(for*/mutable-seteq (for-clause ...) body ...+)
syntax
(for*/mutable-seteqv (for-clause ...) body ...+)
syntax
(for/weak-set (for-clause ...) body ...+)
syntax
(for/weak-seteq (for-clause ...) body ...+)
syntax
(for/weak-seteqv (for-clause ...) body ...+)
syntax
(for*/weak-set (for-clause ...) body ...+)
syntax
(for*/weak-seteq (for-clause ...) body ...+)
syntax
(for*/weak-seteqv (for-clause ...) body ...+)
4.16.2 Set Predicates and Contracts
procedure
(generic-set? v) → boolean?
v : any/c
Examples: | ||||||||
|
procedure
(set-implements? st sym ...) → boolean?
st : generic-set? sym : symbol?
Examples: | ||||||||||||||
|
procedure
(set-implements/c sym ...) → flat-contract?
sym : symbol?
procedure
elem/c : chaperone-contract? cmp : (or/c 'dont-care 'equal 'eqv 'eq) = 'dont-care
kind : (or/c 'dont-care 'immutable 'mutable 'weak 'mutable-or-weak) = 'dont-care
If kind is 'immutable, 'mutable, or 'weak, the resulting contract accepts only hash sets that are respectively immutable, mutable with strongly-held keys, or mutable with weakly-held keys. If kind is 'mutable-or-weak, the resulting contract accepts any mutable "hash sets", regardless of key-holding strength.
If cmp is 'equal, 'eqv, or 'eq, the resulting contract accepts only hash sets that compare elements using equal?, eqv?, or eq?, respectively.
If cmp is 'eqv or 'eq, then elem/c must be a flat contract.
If cmp and kind are both 'dont-care, then the resulting contract will accept any kind of set, not just hash sets.
4.16.3 Generic Set Interface
syntax
Examples: | |||||||||||||||||||||||||||||||
|
4.16.3.1 Set Methods
The methods of gen:set can be classified into three categories, as determined by their fallback implementations:
methods with no fallbacks,
methods whose fallbacks depend on other, non-fallback methods,
and methods whose fallbacks can depend on either fallback or non-fallback methods.
As an example, implementing the following methods would guarantee that all the methods in gen:set would at least have a fallback method:
There may be other such subsets of methods that would guarantee at least a fallback for every method.
procedure
(set-member? st v) → boolean?
st : generic-set? v : any/c
procedure
(set-add st v) → generic-set?
st : generic-set? v : any/c
procedure
st : generic-set? v : any/c
procedure
(set-remove st v) → generic-set?
st : generic-set? v : any/c
procedure
(set-remove! st v) → void?
st : generic-set? v : any/c
procedure
(set-empty? st) → boolean?
st : generic-set?
Supported for any st that implements set->stream or set-count.
procedure
st : generic-set?
Supported for any st that supports set->stream.
procedure
st : (and/c generic-set? (not/c set-empty?))
Supported for any st that implements set->stream.
procedure
(set-rest st) → generic-set?
st : (and/c generic-set? (not/c set-empty?))
Supported for any st that implements set-remove and either set-first or set->stream.
procedure
(set->stream st) → stream?
st : generic-set?
procedure
(set-copy st) → generic-set?
st : generic-set?
Supported for any st that supports set->stream and either implements set-copy-clear and set-add!.
procedure
(set-copy-clear st) → (and/c generic-set? set-empty?)
st : generic-set?
A difference between set-copy-clear and set-clear is that the latter conceptually iterates set-remove on the given set, and so it preserves any contract on the given set. The set-copy-clear function produces a new set without any contracts.
Supported for any st that implements set-remove and supports set->stream.
procedure
(set-clear st) → (and/c generic-set? set-empty?)
st : generic-set?
Supported for any st that implements set-remove and supports set->stream.
procedure
(set-clear! st) → void?
st : generic-set?
Supported for any st that implements set-remove! and either supports set->stream or implements set-first and either set-count or set-empty?.
procedure
(set-union st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the total size of the sts times the size of the result.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the total size of all of the sets except the largest immutable set.
At least one set must be provided to set-union to determine the type of the resulting set (list, hash set, etc.). If there is a case where set-union may be applied to zero arguments, instead pass an empty set of the intended type as the first argument.
Supported for any st that implements set-add and supports set->stream.
Examples: | ||||||||||||||
|
procedure
(set-union! st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the total size of the sts.
Supported for any st that implements set-add! and supports set->stream.
procedure
(set-intersect st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the total size of the sts times the size of st0.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of the smallest immutable set.
Supported for any st that implements either set-remove or both set-clear and set-add, and supports set->stream.
procedure
(set-intersect! st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st0.
Supported for any st that implements set-remove! and supports set->stream.
procedure
(set-subtract st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the total size of the sts times the size of st0.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st0.
Supported for any st that implements either set-remove or both set-clear and set-add, and supports set->stream.
procedure
(set-subtract! st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st0.
Supported for any st that implements set-remove! and supports set->stream.
procedure
(set-symmetric-difference st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the total size of the sts times the size of st0.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the total size of all of the sets except the largest immutable set.
Supported for any st that implements set-remove or both set-clear and set-add, and supports set->stream.
Example: | ||
|
procedure
(set-symmetric-difference! st0 st ...) → generic-set?
st0 : generic-set? st : generic-set?
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the total size of the sts.
Supported for any st that implements set-remove! and supports set->stream.
procedure
st : generic-set? st2 : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the size of st times the size of st2.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st plus the size of st2.
Supported for any st and st2 that both support subset?; also supported for any if st2 that implements set=? regardless of st.
Examples: | ||||||||||||||||
|
procedure
st : generic-set? st2 : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the size of st times the size of st2.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st.
Supported for any st that supports set->stream.
Examples: | ||||||
|
procedure
(proper-subset? st st2) → boolean?
st : generic-set? st2 : generic-set?
If st0 is a list, each st must also be a list. This operation runs on lists in time proportional to the size of st times the size of st2.
If st0 is a hash set, each st must also be a hash set that uses the same comparison function (equal?, eqv?, or eq?). The mutability and key strength of the hash sets may differ. This operation runs on hash sets in time proportional to the size of st plus the size of st2.
Supported for any st and st2 that both support subset?.
Examples: | ||||||
|
procedure
st : generic-set?
Supported for any st that supports set->stream.
Supported for any st that supports set->stream.
procedure
(set-for-each st proc) → void?
st : generic-set? proc : (any/c . -> . any)
Supported for any st that supports set->stream.
procedure
st : generic-set?
Supported for any st that supports set->stream.
4.16.4 Custom Hash Sets
syntax
(define-custom-set-types name optional-predicate comparison-expr optional-hash-functions)
optional-predicate =
| #:elem? predicate-expr optional-hash-functions =
| hash1-expr | hash1-expr hash2-expr
Defines seven names:
name? recognizes instances of the new type,
immutable-name? recognizes immutable instances of the new type,
mutable-name? recognizes mutable instances of the new type with strongly-held elements,
weak-name? recognizes mutable instances of the new type with weakly-held elements,
make-immutable-name constructs immutable instances of the new type,
make-mutable-name constructs mutable instances of the new type with strongly-held elements, and
make-weak-name constructs mutable instances of the new type with weakly-held elements.
The constructors all accept a stream as an optional argument, providing initial elements.
Examples: | ||||||||||||||||||||||||||||||||||||||||||||||
|
procedure
(make-custom-set-types eql? [ hash1 hash2 #:elem? elem? #:name name #:for who])
→
(any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (->* [] [stream?] generic-set?) (->* [] [stream?] generic-set?) (->* [] [stream?] generic-set?)
eql? :
(or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))
hash1 :
(or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) = (const 1)
hash2 :
(or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) = (const 1) elem? : (any/c . -> . boolean?) = (const #true) name : symbol? = 'custom-set who : symbol? = 'make-custom-set-types
The comparison function eql? may accept 2 or 3 arguments. If it accepts 2 arguments, it given two elements to compare them. If it accepts 3 arguments and does not accept 2 arguments, it is also given a recursive comparison function that handles data cycles when comparing sub-parts of the elements.
The hash functions hash1 and hash2 may accept 1 or 2 arguments. If either hash function accepts 1 argument, it is applied to a element to compute the corresponding hash value. If either hash function accepts 2 arguments and does not accept 1 argument, it is also given a recursive hash function that handles data cycles when computing hash values of sub-parts of the elements.
The predicate elem? must accept 1 argument and is used to recognize valid elements for the new set type.
Produces seven values:
a predicate recognizing all instances of the new set type,
a predicate recognizing immutable instances,
a predicate recognizing mutable instances,
a predicate recognizing weak instances,
a constructor for immutable instances,
a constructor for mutable instances, and
a constructor for weak instances.
See define-custom-hash-types for an example.