blob: d5f402079a60ec9992f8004fb388eb4eedcc30ea (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
;;
;; Lists of symbols as sets
;;
;; Essentially, this provides specialized versions of SRFI-1's lset
;; operations for symbols but with linear instead of quadratic time
;; complexity. It's even faster than using a side hash table, unless
;; the symbols already have large plists associated with them.
;;
(declare (disable-interrupts))
(module slsets
(slset<= slset= slset-adjoin
slset-difference slset-intersection
slset-difference+intersection
slset-union slset-xor
slset-deduplicate
slset-delete slset-contains?
with-reified-slset reified-slset? reified-slset->slset
reified-slset-adjoin! reified-slset-delete! reified-slset-contains?)
(import scheme (chicken base) (chicken plist))
(define-inline (mark! x marking)
(when (symbol? x) (put! x marking #t)))
(define-inline (mark-list! lst marking)
(for-each (lambda (x) (mark! x marking)) lst))
(define-inline (unmark! x marking)
(when (symbol? x) (remprop! x marking)))
(define-inline (unmark-list! lst marking)
(for-each (lambda (x) (unmark! x marking)) lst))
(define-inline (with-marked-list lst fun)
(let ((marking (gensym 'm)))
(dynamic-wind
(lambda () (mark-list! lst marking))
(lambda () (fun marking))
(lambda () (unmark-list! lst marking)))))
(define-inline (every? pred lst)
(let lp ((lst lst))
(cond ((null? lst) #t)
((not (pred (car lst))) #f)
(else (lp (cdr lst))))))
(define-inline (marked? x marking)
(and (symbol? x) (get x marking)))
(define (last lst)
(if (null? (cdr lst))
(car lst)
(last (cdr lst))))
(define-inline (filter pred lst)
(let lp ((lst lst)
(res '()))
(cond ((null? lst) (reverse res))
((not (pred (car lst))) (lp (cdr lst) res))
(else (lp (cdr lst) (cons (car lst) res))))))
(define (slset<= . lsts)
(or (null? lsts)
(let ((last-list (last lsts)))
(with-marked-list last-list
(lambda (m)
(every? (lambda (lst)
(or (eq? lst last-list) ; Avoid filtering out this one and building a new list
(every? (lambda (x) (marked? x m)) lst)))
lsts))))))
(define (slset= . lsts)
(or (null? lsts)
(let ((lst (car lsts))
(lsts (cdr lsts)))
(every? (lambda (l)
(and (slset<= lst l) (slset<= l lst)))
lsts))))
(define (slset-adjoin lst . els)
(with-marked-list lst
(lambda (m)
(dynamic-wind
void
(lambda ()
(let lp ((lst lst)
(els els))
(cond ((null? els) lst)
((marked? (car els) m) (lp lst (cdr els)))
(else (mark! (car els) m)
(lp (cons (car els) lst) (cdr els))))))
(lambda ()
(unmark-list! els m))))))
(define (slset-difference lst . lsts)
(let lp ((lsts lsts)
(res lst))
(if (null? lsts)
res
(lp (cdr lsts)
(with-marked-list (car lsts) (lambda (m) (filter (lambda (x) (not (marked? x m))) res)))))))
(define (slset-intersection lst . lsts)
(let lp ((lsts lsts)
(res lst))
(if (null? lsts)
res
(lp (cdr lsts)
(with-marked-list (car lsts)
(lambda (m) (filter (lambda (x) (marked? x m)) res)))))))
;; TODO: Make this faster
(define (slset-difference+intersection lst . lsts)
(values (apply slset-difference lst lsts)
(apply slset-intersection lst lsts)))
;; NOTE: Can't use mark-list here, so roll our own!
;; TODO: Clean up if there's an error cdring down any of these lists?
(define (slset-union . lsts)
(if (null? lsts)
'()
(let ((marking (gensym 'm))
(first-list (car lsts)))
(mark-list! first-list marking)
(let lp ((lsts (cdr lsts))
(res first-list))
(if (null? lsts)
(begin (unmark-list! res marking)
res)
(let lp2 ((lst (car lsts))
(res res))
(if (null? lst)
(lp (cdr lsts) res)
(let ((x (car lst)))
(if (marked? x marking)
(lp2 (cdr lst) res)
(begin (mark! x marking)
(lp2 (cdr lst) (cons x res))))))))))))
(define (slset-xor . lsts)
(let lp ((lsts lsts)
(res '()))
(if (null? lsts)
res
(lp (cdr lsts)
(append (slset-difference res (car lsts))
(slset-difference (car lsts) res))))))
;; NOTE: Can't use mark-list here, so roll our own!
;; TODO: Clean up if there's an error cdring down the list?
(define (slset-deduplicate lst)
(let ((marking (gensym 'm)))
(let lp ((lst lst)
(res '()))
(if (null? lst)
(begin (unmark-list! res marking)
(reverse res))
(let ((x (car lst)))
(cond ((marked? x marking)
(lp (cdr lst) res))
(else (mark! x marking)
(lp (cdr lst) (cons x res)))))))))
;; These are not really necessary - one can use memq and srfi-1's delete.
;; But they're supplied so we have the full gamut of set operations.
;; Also, deletion is a bit more ergonomic as it accepts multiple arguments.
(define (slset-contains? slset el)
(and (memq el slset) #t))
(define (slset-delete slset . els)
(with-marked-list els
(lambda (m)
(filter (lambda (x) (not (marked? x m))) slset))))
(define-record-type :reified-slset
(reified-slset marking slset)
reified-slset?
(marking reified-slset-marking)
(slset reified-slset-list reified-slset-list-set!))
(define reified-slset->slset reified-slset-list)
(define (with-reified-slset lst fun)
;; Note: we can't use with-marked-list here because the reified
;; slset will be mutated in-place, so the enter/leave thunks must
;; extract the *current* list from the reified slset every time.
(let* ((marking (gensym 'm))
(rs (reified-slset marking lst)))
(dynamic-wind
(lambda () (mark-list! (reified-slset-list rs) marking))
(lambda ()
(fun rs)
(reified-slset-list rs))
(lambda () (unmark-list! (reified-slset-list rs) marking)))))
(define (reified-slset-adjoin! rs . els)
(let ((m (reified-slset-marking rs)))
(let lp ((lst (reified-slset-list rs))
(els els))
(cond ((null? els)
(reified-slset-list-set! rs lst)
rs)
((marked? (car els) m) (lp lst (cdr els)))
(else (mark! (car els) m)
(lp (cons (car els) lst) (cdr els)))))))
;; NOTE: Deleting elements not already occurring in the list is O(1)
(define (reified-slset-delete! rs . els)
(let* ((m1 (reified-slset-marking rs))
(els (filter (lambda (e) (not (marked? m1 e))) els)))
(unless (null? els)
(let ((lst (with-marked-list els
(lambda (m2)
(filter (lambda (el) (not (marked? el m2)))
(reified-slset-list rs))))))
(for-each (lambda (e) (unmark! e m1)) els)
(reified-slset-list-set! rs lst)))
rs))
(define (reified-slset-contains? rs el)
(marked? el (reified-slset-marking rs)))
)
|