More ::= reversions in core.
[cslatevm.git] / src / core / collection.slate
blob5ae5098f18c542fb55204a8055afa024ff4f27c4
1 collections addPrototype: #Collection derivedFrom: {Cloneable}.
2 "The abstract object type for Collections, objects which other objects can be
3 added and removed and tested for as elements."
5 "Some common idioms for testing collections."
6 c@(Collection traits) isEmpty
7 "The collection is empty if it contains 0 elements."
8 [c size = 0].
9 c@(Collection traits) isNotEmpty [c isEmpty not].
10 c@(Collection traits) isNotFull [c size < c capacity].
11 c@(Collection traits) isFull [c isNotFull not].
13 c@(Collection traits) ifEmpty: block
14 [c isEmpty ifTrue: [block do]].
16 c@(Collection traits) ifNotEmpty: block
17 [c isEmpty ifFalse: [block do]].
19 c@(Collection traits) ifNotEmptyDo: block
20 [c isEmpty ifFalse: [block apply*, c]].
22 c@(Collection traits) ifEmpty: emptyBlock ifNotEmpty: block
23 [c isEmpty ifTrue: [emptyBlock do] ifFalse: [block do]].
25 c@(Collection traits) ifEmpty: emptyBlock ifNotEmptyDo: block
26 [c isEmpty ifTrue: [emptyBlock do] ifFalse: [block apply*, c]].
28 c@(Collection traits) new &capacity: n
29 "Answer a new Collection based on the given one which isEmpty and has the
30 given capacity if possible."
31 [overrideThis].
33 c@(Collection traits) newSize: n
34 "Answer a new Collection based on the given one with no elements and the given
35 capacity (the size will be 0). Override this for the new* methods to work per
36 collection implementation type."
37 [c new &capacity: n].
39 c@(Collection traits) newEmpty
40 "By default, new collections should be spaced to hold a certain amount,
41 which makes working with small collections frequently less of a pain."
42 [c new].
44 c@(Collection traits) newSizeOf: x
45 "A flexible method which uses the result of whatever size method the second
46 argument defines."
47 [c new &capacity: x size].
49 c@(Collection traits) newSameSize
50 "Returns a new collection of the same size as the given one."
51 [c newSizeOf: c].
53 _@(Collection traits) accepts: obj
54 "Some collections are implemented so that certain types of objects cannot be
55 encoded as elements, in which case, add: or at:put: would fail. Here's the
56 default method."
57 [True].
59 _@(Collection traits) elementType
60 "The acceptable element type of the collection."
61 [Root].
63 _@(Collection traits) defaultElement
64 [Nil].
66 c@(Collection traits) as: d@(Collection traits)
67 "The default conversion between collections uses this method which doesn't
68 get overridden."
69 [d newWithAll: c].
71 c@(Collection traits) newWithAll: d@(Collection traits)
72 "Make a new collection of kind c and stuff in all of d's elements."
74   ((c newSizeOf: d) writer ; d) contents
77 c@(Collection traits) anyOne
78 "Return the first (any) element possible, not a random one."
80   c emptyCheck.
81   c do: [| :each | ^ each].
84 c@(Collection traits) size
85 "Tally up the number of elements in the Collection."
86 [| tally |
87   tally := 0.
88   c do: [| :each | tally += 1].
89   tally
92 c@(Collection traits) capacity
93 "How much can it carry? This does not always equal size, and can change
94 with grow/shrink methods."
95 [c size].
97 c@(Collection traits) hash
98 "Answer an integer hash value for the receiver such that, 
99 the hash value of an unchanged object is constant over time, and 
100 two equal objects have equal hash values."
101 [| result |
102   result := c traits identityHash.
103   c size <= 10
104     ifTrue: [c do: [| :each | result := result bitXor: each hash]].
105   result bitXor: c size hash
108 c@(Collection traits) contents
109 "Answer c's elements. Provides compatibility with other collections with
110 complex implementations."
111 [c].
113 c@(Collection traits) do: block
114 "Evaluate the block once for each element on that value."
115 [overrideThis].
117 c@(Collection traits) do: block separatedBy: sepBlock
118 "Run the separator block between applying the block to each element."
119 [| first |
120   first := True.
121   c do: [| :each |
122     first ifTrue: [first := False] ifFalse: [sepBlock do].
123     block apply*, each].
126 c@(Collection traits) inject: start into: block
127 "Accumulate a running value starting with the input and running a block on it
128 and each element of a collection in turn. The result of the block is
129 implicitly the next value."
130 [| result |
131   result := start.
132   c do: [| :element | result := block apply*, result, element].
133   result
136 c@(Collection traits) collect: block into: d
137 "Answer a new collection resulting from mapping the block onto each element."
139   [| :result | c do: [| :each | result nextPut: (block apply*, each)]]
140      writingAs: d
143 c@(Collection traits) collect: block
144 "Answer a new collection resulting from mapping the block onto each element.
145 This returns a collection of the same kind."
146 [c collect: block into: c].
148 c@(Collection traits) count: test
149 "Return the number of elements of the collection satisfying the test."
150 [| sum |
151   sum := 0.
152   c do: [| :each | (test apply*, each) ifTrue: [sum += 1]].
153   sum
156 c@(Collection traits) detect: succeed ifNone: fail
157 "Find an element satisfying a test. Conditionally execute a failure block."
159   c do: [| :element | (succeed apply*, element) ifTrue: [^ element]].
160   fail do
163 c@(Collection traits) detect: succeed
164 "Supply a default failure block to detect:ifNone: which just answers Nil."
165 [c detect: succeed ifNone: []].
167 c@(Collection traits) anySatisfy: predicate
168 "Answer whether any elements cause the input block to be True."
170   c do: [| :element | (predicate apply*, element) ifTrue: [^ True]].
171   False
174 c@(Collection traits) allSatisfy: predicate
175 "Answer whether all elements cause the input block to be True."
177   c do: [| :element | (predicate apply*, element) ifFalse: [^ False]].
178   True
181 c@(Collection traits) noneSatisfy: predicate
182 "Answer whether none of c's elements cause the input block to be True."
184   c do: [| :element | (predicate apply*, element) ifTrue: [^ False]].
185   True
188 c@(Collection traits) select: test into: result
189 "Write the elements of c satisfying the test block into the given resulting
190 collection."
192   [| :result |
193    c do: [| :each | (test apply*, each) ifTrue: [result nextPut: each]]]
194     writingAs: result
197 c@(Collection traits) select: test
198 "Answer a subset of c containing those elements causing the input block to
199 return True."
200 [c select: test into: c new].
202 c@(Collection traits) select: test collect: block
203 "An optimization for staged collect:'s on select: results."
205   [| :result |
206    c do: [| :each | (test apply*, each)
207                     ifTrue: [result nextPut: (block apply*, each)]]]
208     writingAs: c
211 c@(Collection traits) collect: block select: test
212 "An optimization for staged select:'s on collect: results."
214   [| :result |
215    c do: [| :each tmp |
216           tmp := block apply*, each.
217           (test apply*, tmp) ifTrue: [result nextPut: tmp]]]
218     writingAs: c
221 c@(Collection traits) gather: binBlock &initial: init
222 "Accumulate a running value by:
223 - starting with the optional initial or a default chosen from the collection.
224 - running a block on both it and each remaining element in turn.
225 The result of the block becomes the next value, and then the result when done.
226 e.g. {1. 2. 3 .4} reduce: [| :a :b | a + b] returns a sum of the elements."
227 [| result reader |
228   c isEmpty
229     ifTrue: [init]
230     ifFalse:
231       [reader := c reader.
232        result := init ifNil: [reader next].
233        reader do: [| :each | result := binBlock apply*, result, each].
234        result]
237 c@(Collection traits) reduce: binBlock ifEmpty: emptyBlock
238 "Reduce works like inject except that the first element of the collection is
239 used as the injected element for the rest of the collection.
240 e.g. #{1. 2. 3 .4} reduce: [| :a :b | a + b] returns a sum of the elements."
241 [| result reader |
242   c isEmpty  
243    ifTrue: [emptyBlock do]
244    ifFalse:
245      [result := (reader := c reader) next.
246       reader do: [| :each | result := binBlock apply*, result, each].
247       result]
250 c@(Collection traits) reduce: binBlock
251 "Same as reduce:ifEmpty:, except doing nothing and answering Nil in the empty
252 case."
254   c reduce: binBlock ifEmpty: []
257 c@(Collection traits) trace: binBlock
258 "Like reduce: but returns a Collection of intermediate values."
259 [| elem src |
260   [| :result |
261    src := c reader.
262    elem := src next.
263    result nextPut: elem.
264    src do:
265      [| :each | elem := result nextPut: (each := binBlock apply*, elem, each)]]
266     writingInto: c newSameSize
269 block across: c@(Collection traits)
270 "Apply the block to matching elements of collections in c (so, c
271 should be a collection of collections). If it's a binary block, it
272 works like reduce, otherwise the blocks arity should be the same as
273 the number of collections in c.
274 Example:
275 #+`er across: {{100. 200. 300}. {10. 20. 30}. {1. 2. 3}} => {111. 222. 333}"
277   block arity = 2 
278     ifTrue: [c allCollect: [| *rest | rest reduce: block]]
279     ifFalse: [block arity = c size \/ [block acceptsAdditionalArguments]
280                 ifTrue: [c allCollect: block]
281                 ifFalse: [error: 'Trying to across: using non-binary block that is of different arity than the number of arguments.']]
284 c@(Collection traits) pairCollect: binBlock
285 "Apply binBlock to every (stream-adjacent) pair of c."
287   [| :result src last current |
288    src := c reader.
289    last := src next.
290    src do: [| :current |
291             result nextPut: (binBlock apply*, last, current).
292             last := current]].
293      writingInto: (c new &capacity: c size - 1)
296 c1@(Collection traits) leftCollect: c2@(Collection traits) using: binBlock
297 "Apply binBlock to every element of c1 using c2 as a second argument to the block."
299   c1 collect: [| :item | binBlock apply*, item, c2]
302 c1@(Collection traits) rightCollect: c2@(Collection traits) using: binBlock
303 "Like leftCollect, but using c1 as a left argument to binBlock and elements of c2 each as a second argument."
305   c2 collect: [| :item | binBlock apply*, c1, item]
308 c@(Collection traits) most: binBlock
309 "Answer the element which satisfies the binary comparison with (ideally)
310 all other elements in the Collection. (see also least:)"
311 "TODO: add an optional specifying the value if none is found, useful for when
312 isEmpty implies Nil returns and subsequent necessary checks are not enough."
314   c reduce: [| :a :b | (binBlock apply*, a, b)
315                        ifTrue: [a] ifFalse: [b]]
318 c@(Collection traits) most
319 [c most: #>`er ].
321 c@(Collection traits) least: binBlock
322 "Answer the element which fails the binary comparison with (ideally) all
323 other elements in the Collection. (see also most:)"
324 "TODO: add an optional specifying the value if none is found, useful for when
325 isEmpty implies Nil returns and subsequent necessary checks are not enough."
327   c reduce: [| :a :b | (binBlock apply*, a, b)
328                        ifTrue: [b] ifFalse: [a]]
331 c@(Collection traits) least
332 [c least: #>`er ].
334 c@(Collection traits) reject: test
335 "Answer the complement of select:, a subset of the collection containing those
336 elements causing the input block to return False."
338   c select: [| :each | (test apply*, each) not]
341 c@(Collection traits) copyWithout: obj
342 "Return a new collection with all elements equal to the given ones removed."
344   c reject: #(= obj) `er
347 c@(Collection traits) difference: d@(Collection traits)
348 "Answer the subset of c that are not elements of d."
350   c reject: #(d includes: _) `er
353 c@(Collection traits) intersection: d@(Collection traits)
354 "Answer a collection of kind c with elements that are common to both c and d."
356   c select: #(d includes: _) `er
359 c@(Collection traits) /\ d@(Collection traits)
361   c intersection: d
364 c@(Collection traits) union: d@(Collection traits)
365 "Answer a Set with elements from both input collections."
367   ((c as: Set) writer ; d) contents
370 c@(Collection traits) \/ d@(Collection traits)
372   c union: d
375 c@(Collection traits) includes: obj
376 "Return whether some object equal to the input is in the collection."
378   c anySatisfy: #(= obj) `er
381 c@(Collection traits) identityIncludes: obj
382 "Return whether the input object is in the collection."
384   c anySatisfy: #(== obj) `er
387 c@(Collection traits) includesAllOf: d
388 "Return whether d is a subset of c. All elements of d are in c."
390   d do: [| :each | (c includes: each) ifFalse: [^ False]].
391   True
394 c@(Collection traits) includesAnyOf: d
395 "Return whether the collections have any common elements."
397   d do: [| :each | (c includes: each) ifTrue: [^ True]].
398   False
401 c@(Collection traits) occurrencesOf: obj
402 "Return how many times the input object occurs in the collection."
403 [| tally |
404   tally := 0.
405   c do:
406     [| :each | obj = each ifTrue: [tally += 1]].
407   tally