Core code source usage of ::= and other cleanups.
[cslatevm.git] / src / core / sequence.slate
blobb0370365771a3547299b37ca42a7b2f1e5b9edac
1 collections define: #Sequence &parents: {Collection. Mapping}.
2 "Sequences are Mappings from a range of Integers starting from 0 to a final
3 Integer, in a sequence, to arbitrary objects. The last address will be one
4 less than the size."
6 "Mapping compatibility methods."
8 _@Sequence size [0]. "metadata traits barrier"
10 s@(Sequence traits) acceptsKey: n@(Integer traits)
11 [n isPositive /\ [n < s size]].
13 _@(Sequence traits) acceptsKey: _
14 [False].
16 s@(Sequence traits) keySet
17 "Answers the keys of a Sequence as a Range; will not work until Range is
18 installed."
20   s isEmpty ifTrue: [#{}] ifFalse: [0 below: s size]
23 s@(Sequence traits) keysDo: block
24 "Iterate over just the indices in the Sequence."
26   0 below: s size do: [| :index | block apply*, index]
29 s@(Sequence traits) keysAndValuesDo: block
31   s doWithIndex: [| :each :index | block apply*, index, each]
34 "Some commonly useful idioms."
36 c@(Sequence traits) first [c at: 0].
37 c@(Sequence traits) second [c at: 1].
38 c@(Sequence traits) third [c at: 2].
39 c@(Sequence traits) fourth [c at: 3].
40 c@(Sequence traits) fifth [c at: 4].
41 c@(Sequence traits) sixth [c at: 5].
42 c@(Sequence traits) seventh [c at: 6].
43 c@(Sequence traits) eighth [c at: 7].
44 c@(Sequence traits) ninth [c at: 8].
45 c@(Sequence traits) tenth [c at: 9].
47 c@(Sequence traits) indexFirst [0].
48 c@(Sequence traits) indexLast [c size - 1].
49 "The index of the last element, not to be confused with lastIndex for
50 ExtensibleArray which indexes into its internal Array."
51 c@(Sequence traits) last [c at: c indexLast].
52 c@(Sequence traits) indexMiddle [c size // 2].
53 "The median index."
54 c@(Sequence traits) middle
55 "Answer the element at the median index."
56 [c at: c indexMiddle].
58 s@(Sequence traits) newWith: obj
59 [(s new &capacity: 1) `>> [at: s indexFirst put: obj. ]].
61 s@(Sequence traits) newWithAll: c@(Collection traits)
62 [| index |
63   result ::= s newSizeOf: c.
64   index := 0.
65   c do: [| :each | result at: index put: each. index += 1].
66   result
69 s@(Sequence traits) newWithAll: c@(Sequence traits)
71   (s new &capacity: c size `cache) `>> [replaceFrom: 0 below: c size with: c. ]
74 s1@(Sequence traits) as: s2@(Sequence traits)
76   s2 newWithAll: s1
79 s@(Sequence traits) as: ea@(ExtensibleCollection traits)
81   (ea newSizeOf: s) `>> [addAll: s. ]
84 assoc@(Association traits) as: s@(Sequence traits)
85 [{assoc key. assoc value}].
87 s@(Sequence traits) as: assoc@(Association traits)
89   s size = 2
90     ifTrue: [s first -> s second]
91     ifFalse: [error: 'Invalid conversion - the input Sequence is not a pair.']
94 s@(Sequence traits) new* [| *rest | rest as: s].
96 s@(Sequence traits) length
97 "For Sequences, length is a property we expect - it's the size of it."
98 [s size].
100 c@(Sequence traits) arrayType
101 "A generic protocol for returning a suitable Array prototype for holding the
102 Sequence's objects. TODO: This is an internal, messy kind of detail that
103 should be fixed with some parametrization on elementType."
104 [Array].
106 c@(Sequence traits) after: obj ifAbsent: block
108   (c indexOf: obj)
109     ifNil: [block do]
110     ifNotNilDo:
111       [| :index | index = c indexLast ifFalse: [c at: index + 1]]
114 c@(Sequence traits) after: obj
116   c after: obj ifAbsent: [Nil]
119 c@(Sequence traits) allButFirst: n
121   n < c size
122     ifTrue: [c copyFrom: n to: c indexLast]
123     ifFalse: [c new]
126 c@(Sequence traits) allButFirst
128   c allButFirst: 1
131 c@(Sequence traits) allButLast: n
133   n < c size
134     ifTrue: [c copyFrom: 0 to: c size - n - 1]
135     ifFalse: [c new]
138 c@(Sequence traits) allButLast
140   c allButLast: 1
143 c@(Sequence traits) anyOne
145   c first
148 c@(Sequence traits) acceptsKey: n@(Integer traits)
149 "Note that this method is a temporal query: it answers about the 'now'."
151   n >= 0 /\ [n < c size]
154 _@(Sequence traits) acceptsKey: _@(Root traits)
155 "Sequenceables are keyed by integers as indices only."
156 [False].
158 c@(Sequence traits) includesKey: index
159 "A specialized version for sequences"
161   index >= 0 /\ [index < c size]
164 c@(Sequence traits) at: index ifAbsent: block
166   (c includesKey: index)
167     ifTrue: [c at: index]
168     ifFalse: [block do]
171 c@(Sequence traits) atAll: d@(Collection traits)
172 "Returns a new Sequence of the elements corresponding to the indexes in the
173 given Collection."
175   d collect: #(c at: _) `er into: (c newSizeOf: d)
178 c@(Sequence traits) atAll: d@(Sequence traits)
179 "Returns a new Sequence of the elements corresponding to the indexes in the
180 given Sequence."
182   (result ::= c newSizeOf: d) keysDo:
183     [| :index |
184      result at: index put: (c at: (d at: index))].
185   result
188 c@(Sequence traits) atAll: d put: obj
189 "Replace the element at each index with the corresponding value of the
190 values collection."
192   d do: #(c at: _ put: obj) `er.
193   obj
196 c@(Sequence traits) atAll: indices@(Sequence traits) put: values
197 "Replace the element at each index with the corresponding value of the
198 values collection."
200   indices with: values do: #(c at: _ put: _) `er.
201   values
204 c@(Sequence traits) atAllPut: obj
205 "Replace all elements with the given one."
207   c infect: [| :_ | obj]
210 s@(Sequence traits) clear
211 "Set all elements to be the default/clear marker (usually Nil)."
213   s atAllPut: s defaultElement
216 c@(Sequence traits) = d@(Sequence traits)
217 "Tests for equality with the simple == and size-checks first, then iterating
218 through the elements in a linear scan =-comparison."
220   c == d
221    \/ [c size `cache = d size
222          /\ [0 below: c size do:
223            [| :index | (c at: index) = (d at: index) ifFalse: [^ False]].
224              True]]
227 c@(Sequence traits) hash
229   c inject: c traits identityHash into: [| :hash :each | (hash + each hash) hashMultiply]
232 c@(Sequence traits) atPin: index
233 "Return the indexed element, coercing index to the collection's range."
235   c isEmpty ifFalse: [c at: (index min: c indexLast max: 0)]
238 c@(Sequence traits) atWrap: index
239 "Return the indexed element, wrapping the index around until it's in range."
241   c at: index \\ c size
244 c@(Sequence traits) atWrap: index put: obj
245 "Set the indexed element, wrapping the index around until it's in range."
247   c at: index \\ c size put: obj
250 c@(Sequence traits) before: obj ifAbsent: block
252   (c indexOf: obj)
253     ifNil: [block do]
254     ifNotNilDo:
255       [| :index | index isZero ifTrue: [Nil] ifFalse: [c at: index - 1]]
258 c@(Sequence traits) before: obj
260   c before: obj ifAbsent: [Nil]
263 c@(Sequence traits) first: n
264 "Answer the first N elements or the Sequence itself if it is smaller."
266   c copyFrom: 0 to: (n min: c size) - 1
269 c@(Sequence traits) identityIndexOf: obj ifAbsent: block
271   0 below: c size do: [| :index |
272     (c at: index) == obj ifTrue: [^ index]].
273   block do
276 c@(Sequence traits) identityIndexOf: obj
278   c identityIndexOf: obj ifAbsent: [Nil]
281 c@(Sequence traits) indexOf: obj startingAt: start ifAbsent: block
282 "Returns the first occurrence of an element equal to the given object from
283 the start-point. Execute and return the block's value if nothing is found."
285   start below: c size do: [| :index |
286     (c at: index) = obj ifTrue: [^ index]].
287   block do
290 c@(Sequence traits) indexOf: obj startingAt: start
292   c indexOf: obj startingAt: start ifAbsent: [Nil]
295 c@(Sequence traits) indexOf: obj ifAbsent: block
297   c indexOf: obj startingAt: 0 ifAbsent: block
300 c@(Sequence traits) indexOf: obj
302   c indexOf: obj ifAbsent: [Nil]
305 c@(Sequence traits) indicesOfAllSatisfying: block
306 "Answer all positions of the occurrence of elements satisfying the block within
307 the Sequence in the order that they occur."
308 [| position |
309   result ::= ExtensibleArray new.
310   position := 0.
311   [(position := c indexOfFirstSatisfying: block startingAt: position) isNil]
312     whileFalse: [result addLast: position. position += 1].
313   result
316 c@(Sequence traits) indicesOf: obj
317 "Answer all positions of the occurrence of the given element within the
318 Sequence in the order that they occur."
319 [c indicesOfAllSatisfying: #(= obj) `er].
321 c@(Sequence traits) includes: obj
322 "Defined using indexOf: since Sequence is not hashed. Of course, this takes
323 O(n) time in worst cases (most of them, when the element isn't found."
325   (c indexOf: obj) isNotNil
328 c@(Sequence traits) indexOfSubSeq: subSeq startingAt: start ifAbsent: block
330   `conditions: (
331     [subSeq isEmpty] -> [block do].
332     [subSeq size = 1] -> [c indexOf: subSeq first startingAt: start ifAbsent: block]
333   ) otherwise:
334     [| first index |
335      first := subSeq first.
336      start upTo: c size - subSeq size do: [| :startIndex |
337        (c at: startIndex) = first ifTrue:
338          [index := 1.
339           [(c at: startIndex + index) = (subSeq at: index)]
340             whileTrue: [(index += 1) = subSeq size ifTrue: [^ startIndex]]]].
341      block do]
344 c@(Sequence traits) indexOfSubSeq: subSeq startingAt: start
346   c indexOfSubSeq: subSeq startingAt: start ifAbsent: [Nil]
349 c@(Sequence traits) indexOfSubSeq: subSeq
351   c indexOfSubSeq: subSeq startingAt: 0
354 c@(Sequence traits) includesSubSeq: subSeq
356   (c indexOfSubSeq: subSeq) isNotNil
359 c@(Sequence traits) last: n
360 "Answer the last N elements in the Sequence or itself if smaller."
362   c copyFrom: (0 max: c size - n)
365 c@(Sequence traits) lastIndexOf: obj startingAt: start ifAbsent: block
366 "Answer the last index where an element equal to the object is found
367 preceding the start index, executing the block if none."
369   start downTo: 0 do: [| :index | (c at: index) = obj ifTrue: [^ index]].
370   block do
373 c@(Sequence traits) lastIndexOf: obj ifAbsent: block
375   c lastIndexOf: obj startingAt: c indexLast ifAbsent: block
378 c@(Sequence traits) lastIndexOf: obj
379 "The last index where the object is found or Nil if none."
381   c lastIndexOf: obj startingAt: c indexLast ifAbsent: [Nil]
384 s@(Sequence traits) firstIndexOf: delims@(Collection traits) startingAt: start
385 "Answer the first occurrence of an object in the delimiters, starting at
386 start, returning size on failure."
388   start below: s size do:
389     [| :index | delims do:
390        [| :obj | obj = (s at: index) ifTrue: [^ index]]].
391   s size
394 s@(Sequence traits) lastIndexOf: delims@(Collection traits) startingAt: start
395 "Answer the last occurrence of an object in the delimiters, starting at
396 start, returning -1 on failure."
398   start above: 0 do:
399     [| :index | delims do:
400        [| :obj | obj = (s at: index) ifTrue: [^ index]]].
401   -1
404 c@(Sequence traits) replaceAll: obj with: newOne
405 "Replace all elements equal to the given object with the replacement given."
406 [| index |
407   index := 0.
408   [(index := c indexOf: obj startingAt: index) isNil]
409     whileFalse: [c at: index put: newOne].
412 c@(Sequence traits) replaceFrom: start below: end with: replacement startingAt: repStart
414   c replaceFrom: start to: end - 1 with: replacement startingAt: repStart
417 c@(Sequence traits) replaceFrom: start below: end with: replacement &startingAt: repStart
419   c replaceFrom: start to: end - 1 with: replacement startingAt: (repStart ifNil: [0])
422 c@(Sequence traits) replaceFrom: start to: end with: replacement &startingAt: repStart
424   c replaceFrom: start to: end with: replacement startingAt: (repStart ifNil: [0])
427 c@(Sequence traits) replaceFrom: start to: end with: replacement startingAt: repStart
428 "This destructively modifies the Sequence in a batch operation. It takes care
429 to stay within the range of indices specified and can start from a specified
430 index in the replacement source."
432   repOff ::= repStart - start.
433   c == replacement /\ [start > repStart]
434     ifTrue: [end downTo: start do:
435       [| :index | c at: index put: (replacement at: repOff + index)]]
436     ifFalse: [start upTo: end do:
437       [| :index | c at: index put: (replacement at: repOff + index)]].
440 c@(Sequence traits) replaceFrom: start with: replacement
441 "Destructively modify the Sequence, beginning at start, with all the elements
442 of the replacement that will fit."
444   c replaceFrom: start to: start + replacement indexLast with: replacement
447 c@(Sequence traits) swap: index1 with: index2
448 "Swaps the objects stored at the given indices."
450   obj ::= c at: index1.
451   c at: index1 put: (c at: index2).
452   c at: index2 put: obj.
455 c@(Sequence traits) ; d@(Sequence traits)
456 "Concatenates the two Sequences, answering the result."
458   result ::= c newSize: c size `cache + d size.
459   result replaceFrom: 0 to: c indexLast with: c.
460   result replaceFrom: c size to: result indexLast with: d.
461   result
464 c@(Sequence traits) concatenateAll: seq &separator: delim
465 "Answer a new Sequence the result of concatenating the first argument with
466 all of those in the second, interspersed with elements of the specified
467 delimiter Sequence (or nothing when not given)."
468 [| targetIndex |
469   seq ifNil: [^ c].
470   delim `defaultsTo: #{}.
471   result ::= c new &capacity:
472     (seq inject: c size into:
473        [| :accum :each | accum + each size])
474     + (seq size - (c isEmpty ifTrue: [1] ifFalse: [0]) * delim size).
475   targetIndex := 0.
476   c isEmpty ifFalse:
477     [result replaceFrom: targetIndex below: c size with: c.
478      targetIndex: c size.
479      result replaceFrom: targetIndex below: targetIndex + delim size with: delim.
480      targetIndex += delim size].
481   seq do:
482     [| :each eachSize |
483      eachSize := each size.
484      result replaceFrom: targetIndex below: targetIndex + eachSize with: each.
485      targetIndex += eachSize]
486       separatedBy:
487         [result replaceFrom: targetIndex below: targetIndex + delim size with: delim.
488          targetIndex += delim size].
489   result
492 c@(Sequence traits) ;* seq &separator: delim
493 [c concatenateAll: seq &separator: delim].
495 c@(Sequence traits) concatenatedTimes: n
496 "Answer a Sequence the result of concatenating N copies of the original together."
498   result ::= c new &capacity: c size * n.
499   0 below: result capacity by: c size do:
500     [| :index |
501      c doWithIndex:
502        [| :each :srcIndex |
503         result at: index + srcIndex put: each]].
504   result
507 c@(Sequence traits) copyAfter: obj
508 "Answer a similar Sequence of all but the elements before the first matching
509 the given object."
511   c allButFirst: (c indexOf: obj ifAbsent: [^ c traits new]) + 1
514 c@(Sequence traits) copyAfterLast: obj
515 "Answer a similar Sequence of all but the elements before the last matching
516 the given object."
518   c allButFirst: (c lastIndexOf: obj ifAbsent: [^ c traits new]) + 1
521 c@(Sequence traits) copyFrom: start below: end
523   c copyFrom: start to: end - 1
526 c@(Sequence traits) copyFrom: start to: end
527 "Answer a similar Sequence with the elements from the start to the end
528 indices, inclusive."
530   end < start \/ [c isEmpty]
531     ifTrue: [c new]
532     ifFalse: [(c new &capacity: (newSize ::= end - start + 1))
533                 replaceFrom: 0 to: newSize - 1 with: c startingAt: start]
536 c@(Sequence traits) copyFrom: start
537 [c copyFrom: start to: c indexLast].
539 c@(Sequence traits) copyReplaceFrom: start to: end with: d
540 "Copy with replacement, except if end < start, then this is instead an
541 insertion. start = 0 and end = -1 inserts at the beginning; start = size
542 appends at the end."
544   resultSize ::= c size + d size `cache - (end - start + 1).
545   endReplace ::= start - 1 + d size.
546   result ::= c new &capacity: resultSize.
547   start > 0 ifTrue:
548     [result replaceFrom: 0 to: start - 1 with: c].
549   start <= endReplace ifTrue:
550     [result replaceFrom: start to: endReplace with: d].
551   endReplace < resultSize ifTrue:
552     [result replaceFrom: endReplace + 1 to: resultSize - 1 with: c startingAt: end + 1].
553   result
556 c@(Sequence traits) copyReplaceAll: seq with: replacement
557 "Answer a copy with any occurrences of the (sub)sequence replaced with the
558 given replacement."
559 [| start result currentIndex end |
560   start := 0.
561   result := c.
562   [(currentIndex := result indexOfSubSeq: seq startingAt: start) isNotNil]
563     whileTrue:
564       [end := currentIndex + seq indexLast.
565        result := result copyReplaceFrom: currentIndex to: end with: replacement.
566        start := currentIndex + replacement size].
567   result
570 c@(Sequence traits) copyReplace: element with: obj
571 "Answer a copy with any occurrences of the element replaced with the given
572 object."
574   result ::= c newSameSize.
575   c doWithIndex:
576     [| :each :index |
577      result at: index put: (each = element ifTrue: [obj] ifFalse: [each])].
578   result
581 c@(Sequence traits) copyUpTo: obj
583   c first: (c indexOf: obj ifAbsent: [^ c copy])
586 c@(Sequence traits) copyUpToLast: obj
588   c first: (c lastIndexOf: obj ifAbsent: [^ c copy])
591 c@(Sequence traits) copyWith: obj
592 "Non-destructively append an object."
594   (c new &capacity: (cs ::= c size) + 1) `>>
595    [replaceFrom: 0 below: cs with: c.
596     at: cs put: obj. ]
599 c@(Sequence traits) copyWith: obj at: index
600 "Non-destructively insert an object at a specified index."
602   (c new &capacity: (cs ::= c size) + 1) `>>
603    [replaceFrom: 0 below: index with: c.
604     at: index put: obj.
605     replaceFrom: index + 1 to: cs with: c startingAt: index. ]
608 c@(Sequence traits) copyWithoutFirst
610   c allButFirst
613 c@(Sequence traits) copyWithoutAt: index
615   (c new &capacity: (ns ::= c indexLast)) `>>
616    [replaceFrom: 0 to: index - 1 with: c.
617     replaceFrom: index to: ns - 1 with: c startingAt: index + 1. ]
620 s@(Sequence traits) indexPast: obj startingAt: start
621 "Answer the index of the character within s, starting at start,
622 that does NOT match obj. If s does not contain obj, answer size."
624   start below: s size do:
625     [| :index | obj = (s at: index) ifFalse: [^ index]].
626   s size
629 s@(Sequence traits) indexPastAll: delims startingAt: start
630 "Answer the index of the character within the receiver, starting at start,
631 that does NOT match one of the delimiters. If the receiver does not
632 contain any of the delimiters, answer size. Assumes the delimiters to
633 be a non-empty string."
635   start below: s size do:
636     [| :index |
637      delims detect: [| :c | c  = (s at: index)]
638        ifNone: [^ index]].
639   s size
642 s@(Sequence traits) indexOfAny: delims startingAt: start
643 "Answer the index of the first element in s, starting at start,
644 that matches any of the delimiters.  If s does not contain any of
645 the delimiters, answer s size."
647   start below: s size do:
648     [| :index |
649      delims do:
650        [| :delim | delim = (s at: index) ifTrue: [^ index]]].
651   s size
654 s@(Sequence traits) splitWith: obj &count: count &includeEmpty: empties
655 "Divides the Sequence up into subsequences as delimited by the given element."
656 "NOTE: This has a forward reference to ExtensibleArray."
657 [| keyStart keyEnd |
658   empties `defaultsTo: False.
659   subSeqs ::= ExtensibleArray new.
660   keyEnd := s indexOf: obj startingAt: 0 ifAbsent: [s size].
661   empties \/ [keyEnd > 0] ifTrue: [subSeqs add: (s copyFrom: 0 to: keyEnd - 1)].
662   [keyEnd < s size] whileTrue:
663     [count isNotNil /\ [subSeqs size >= count] ifTrue:
664        [subSeqs add: (s copyFrom: keyEnd to: s indexLast).
665         ^ subSeqs].
666      keyStart := (s indexOf: obj startingAt: keyEnd ifAbsent: [keyEnd]) + 1.
667      keyEnd := s indexOf: obj startingAt: keyStart ifAbsent: [s size].
668      (keyStart < keyEnd) \/ [empties /\ [keyStart <= keyEnd]] ifTrue:
669        [subSeqs add: (s copyFrom: keyStart to: keyEnd - 1)]].
670   subSeqs
673 s@(Sequence traits) splitWithAny: delims@(Sequence traits) &count: count
674 "Answer the Sequence of substrings resulting from splitting the string with
675 the given delimiting characters."
676 [| keyStart keyEnd |
677   subSeqs ::= ExtensibleArray new.
678   keyEnd := 0.
679   [keyEnd < s size] whileTrue:
680     [count isNotNil /\ [subSeqs size >= count]
681        ifTrue: [subSeqs add: (s copyFrom: keyEnd to: s indexLast).
682                 ^ subSeqs].
683      keyStart := s indexPastAll: delims startingAt: keyEnd.
684      keyEnd := s indexOfAny: delims startingAt: keyStart.
685      keyStart < keyEnd
686        ifTrue: [subSeqs add:
687                   (s copyFrom: keyStart to: keyEnd - 1)]].
688   subSeqs
691 c@(Sequence traits) copy
692 "An abstract Sequence copy."
694   c copyFrom: 0 to: c indexLast
697 s@(Sequence traits) copySize: n
698 "Create a new Sequence of the same type and given size, filling in the values
699 with repetitions of the source's contents throughout its length."
701   s size `cache.
702   result ::= s new &capacity: n.
703   result keysDo: [| :index | result at: index put: (s at: index // s size)].
704   result
707 c@(Sequence traits) choose: k of: n into: workingArray do: block
708 "Execute the block on all the combinations of size k out of the sequence.
709 This uses a single array as an iterator object which should not be modified
710 by the client code."
712   `conditions: (
713     [n < k] -> [c].
714     [k isZero] -> [block apply*, workingArray]
715   ) otherwise:
716     [workingArray at: k - 1 put: (c at: n - 1).
717      c choose: k - 1 of: n - 1 into: workingArray do: block.
718      c choose: k of: n - 1 into: workingArray do: block. ]
721 c@(Sequence traits) choose: n do: block
722 "Execute the block on all the combinations of size N out of the sequence.
723 This uses a single array as an iterator object which should not be modified
724 by the client code."
726   c choose: n of: c size into: (Array newSize: n) do: block
729 c@(Sequence traits) collect: block
731   c collectWithIndex: [| :each :_ | block apply*, each] into: c newSameSize
734 c@(Sequence traits) collect: block from: start to: end
735 [| j |
736   end < start \/ [c isEmpty]
737     ifTrue: [c new]
738     ifFalse:
739       [result ::= c new &capacity: end - start + 1.
740        j := start.
741        result keysDo: [| :index | result at: index put: (block apply*, (c at: j)). j += 1].
742        result]
745 c@(Sequence traits) collectWithIndex: binBlock
746 "binBlock should take :element and :index."
748   c collectWithIndex: binBlock into: c newSameSize
751 c@(Sequence traits) do: block
753   c isEmpty ifFalse:
754     [0 below: c size do: [| :index | block apply*, (c at: index)]].
757 c@(Sequence traits) pairsDo: block
758 "Apply the block to each distinct pair of sequential elements. The Sequence
759 must have an even size."
761   (c size rem: 2) isZero
762     ifTrue: [0 below: c size by: 2 do:
763                [| :index | block apply*, (c at: index), (c at: index + 1)]]
764     ifFalse: [error: 'This collection has an odd number of elements'].
767 c@(Sequence traits) chainPairsDo: block
768 "Apply the block to each distinct pair of sequential elements in a
769 next-last pairing so that each element is an argument twice, once in each
770 position (except the first and last elements, of course)."
772   c indexFirst below: c indexLast do:
773     [| :index | block apply*, (c at: index), (c at: index + 1)].
776 c@(Sequence traits) do: block separatedBy: sepBlock
777 "Run the separator block between applying the block to each element."
779   c isEmpty ifFalse:
780     [block apply*, c first.
781      1 below: c size do: [| :index | sepBlock do. block apply*, (c at: index)]].
784 c@(Sequence traits) doWithIndex: block separatedBy: sepBlock
785 "Run the separator block between applying the block to each element."
787   c isEmpty ifFalse:
788     [block apply*, c first, 0.
789      1 below: c size do: [| :index | sepBlock do. block apply*, (c at: index), index]].
792 c@(Sequence traits) from: start to: end do: block
794   (start between: c indexFirst and: end) /\ [end <= c indexLast] ifTrue:
795     [start to: end do: [| :index | block apply*, (c at: index)]].
798 c@(Sequence traits) allButFirst: n do: block
799 "Apply the block to all elements but the first N in the Sequence."
801   c from: n to: c indexLast do: block
804 c@(Sequence traits) allButFirstDo: block
806   c allButFirst: 1 do: block
809 c@(Sequence traits) allButLast: n do: block
810 "Apply the block to all but the last N members of the Sequence."
812   c from: 0 to: c indexLast - n do: block
815 c@(Sequence traits) allButLastDo: block
817   c allButLast: 1 do: block
820 c@(Sequence traits) doWithIndex: binBlock
821 "binBlock takes :each object and its :index."
823   c isEmpty ifFalse:
824     [0 below: c size do:
825       [| :index | binBlock apply*, (c at: index), index]].
828 c@(Sequence traits) collectWithIndex: binBlock into: seq@(Sequence traits)
829 "Runs doWithIndex: on binBlock, collecting results implicitly into the other
830 Sequence at the same locations as the arguments, and returning that result."
832   c doWithIndex:
833     [| :each :index | seq at: index put: (binBlock apply*, each, index)].
834   seq
837 c@(Sequence traits) do: block every: step
838 "Invoke the block on every n'th argument."
840   c do: block every: step startingAt: 0
843 c@(Sequence traits) do: block every: step startingAt: start
844 "Invoke the block on every n'th argument, starting at a particular index."
845 [| index |
846   c isEmpty ifFalse:
847     [index := start.
848      (c size - start) // step timesRepeat:
849        [block apply*, (c at: index). index += step]].
852 c@(Sequence traits) do: block inGroupsOf: arity
853 "Send the elements of the target to the block in the number of its arity. If
854 fewer than that are remaining, abort and return."
856   c do: block inGroupsOf: arity startingAt: 0
859 c@(Sequence traits) do: block inGroupsOf: arity startingAt: start
860 "Send the elements of the target to the block in the number of its arity,
861 beginning with a given index. If fewer than that are remaining, abort and
862 return."
863 [| index |
864   index := start.
865   group ::= Array newSize: arity.
866   (c size - start) // arity timesRepeat:
867     [group replaceFrom: 0 below: arity with: c startingAt: index.
868      block applyTo: group.
869      index += arity].
872 c@(Sequence traits) indexOfFirstSatisfying: block startingAt: start
873 "Find the index of the first occurrence of an object satisfying the block
874 starting at the given index."
876   start below: c size do:
877     [| :index | (block apply*, (c at: index)) ifTrue: [^ index]].
880 c@(Sequence traits) indexOfFirstSatisfying: block
881 "Find the index of the first occurrence of an object satisfying the block."
883   c indexOfFirstSatisfying: block startingAt: 0
886 c@(Sequence traits) indexOfLastSatisfying: block before: end
887 "Find the index of the last occurrence of an object satisfying the block
888 before the given index."
890   end - 1 downTo: 0 do: [| :index |
891     (block apply*, (c at: index)) ifTrue: [^ index]].
894 c@(Sequence traits) indexOfLastSatisfying: block
895 "Find the index of the last occurrence of an object satisfying the block."
897   c indexOfLastSatisfying: block before: c size
900 c@(Sequence traits) firstSatisfying: block startingAt: start
901 "Answer the first occurrence of an object satisfying the block."
902 [c at: ((c indexOfFirstSatisfying: block startingAt: start) ifNil: [^ Nil])].
904 c@(Sequence traits) firstSatisfying: block
905 "Answer the first occurrence of an object satisfying the block."
906 [c at: ((c indexOfFirstSatisfying: block) ifNil: [^ Nil])].
908 c@(Sequence traits) lastSatisfying: block before: end
909 "Answer the last occurrence of an object satisfying the block."
910 [c at: ((c indexOfLastSatisfying: block before: end) ifNil: [^ Nil])].
912 c@(Sequence traits) lastSatisfying: block
913 "Answer the last occurrence of an object satisfying the block."
914 [c at: ((c indexOfLastSatisfying: block) ifNil: [^ Nil])].
916 c@(Sequence traits) reverseDoWithIndex: block
917 "The reverse performance of doWithIndex: of course."
919   c indexLast downTo: 0 do: [| :index | block apply*, (c at: index), index].
922 c@(Sequence traits) reverseDo: block
923 "The reverse performance of do:."
925   c indexLast downTo: 0 do: [| :index | block apply*, (c at: index)].
928 c@(Sequence traits) reverseWith: d do: block
929 "The reverse performance of with:do:."
931   1 to: (c size `cache min: d size `cache) do:
932     [| :index |
933      block apply*, (c at: c size - index), (d at: d size - index)].
936 c@(Sequence traits) shiftFrom: index by: steps count: items
937 "Shifts 'count:' elements from index by the specified amount, positive
938 indicating right-ward. Overwritten elements are shifted back-ward."
940   items := items abs.
941   elems ::= c copyFrom: index below: index + items.
942   steps isNegative ifTrue:
943     [steps := steps abs. index += items - 1. items := items negated].
944   steps timesRepeat: [
945     c at: index put: (c at: index + items).
946     index += items sign].
947   items isNegative ifTrue: [index += items + 1].
948   c replaceFrom: index with: elems
951 c@(Sequence traits) shiftFrom: index by: steps
952 "See shiftFrom:by:count:"
954   c shiftFrom: index by: steps count: 1
957 c@(Sequence traits) rotate &times: count
958 "Rotate all elements in the Sequence in-place by the specified amount, positive
959 indicating right-ward (increasing indices of elements until wrapped around)."
960 [| index steps |
961   count `defaultsTo: 1.
962   count := count mod: c size.
963   count isPositive
964     ifTrue: [index := c size - count. steps := count - c size]
965     ifFalse: [index := 0. steps := c size + count].
966   c shiftFrom: index by: steps count: count
969 c@(Sequence traits) rotated &times: count
970 "Answer a new Sequence of the same size with the elements rotated by the
971 specified amount, positive indicating right-ward (increasing indices of
972 elements until wrapped around)."
973 [| index |
974   count `defaultsTo: 1.
975   c size isZero \/ count isZero
976     ifTrue: [c copy]
977     ifFalse:
978       [count := count mod: c size.
979        index := c size - count mod: c size.
980        c newSameSize `>>
981          [replaceFrom: 0 with: (c sliceFrom: index).
982           replaceFrom: c size - index with: c. ]]
985 c@(Sequence traits) mapSelect: block
986 [c mapSelect: block into: c newSameSize].
988 c@(Sequence traits) with: d@(Sequence traits) collect: binBlock
989 "Collect the result from applying the block to as many pairs of elements from
990 each Sequence as possible."
992   result ::= c new &capacity: (c size min: d size).
993   result keysDo: [| :index |
994     result at: index put: (binBlock apply*, (c at: index), (d at: index))].
995   result
998 c@(Sequence traits) with: d@(Sequence traits) do: binBlock
999 "Apply the block to as many pairs of elements from each Sequence as possible."
1001   0 below: (c size min: d size) do: [| :index |
1002     binBlock apply*, (c at: index), (d at: index)].
1005 c@(Sequence traits) gather: binBlock &initial: init
1006 [| result |
1007   c isEmpty
1008     ifTrue: [init]
1009     ifFalse:
1010       [init
1011          ifNil:
1012            [result := c first.
1013             c allButFirstDo:
1014               [| :each | result := binBlock apply*, result, each]]
1015          ifNotNil:
1016            [result := init.
1017             c do: [| :each | result := binBlock apply*, result, each]].
1018        result]
1021 c@(Sequence traits) reverseGather: binBlock &initial: init
1022 [| result |
1023   c isEmpty
1024     ifTrue: [init]
1025     ifFalse:
1026       [init
1027          ifNil:
1028            [result := c last.
1029             c reverseAllButLastDo:
1030               [| :each | result := binBlock apply*, result, each]]
1031          ifNotNil:
1032            [result := init.
1033             c reverseDo: [| :each | result := binBlock apply*, result, each]].
1034        result]
1037 c@(Sequence traits) reduce: binBlock ifEmpty: emptyBlock
1038 "Reduce works like inject except that the first element of the collection is
1039 used as the injected element for the rest of the collection.
1040 e.g. #{1. 2. 3 .4} reduce: [| :a :b | a + b] returns a sum of the elements."
1041 [| result |
1042   c isEmpty
1043     ifTrue: [emptyBlock do]
1044     ifFalse:
1045       [result := c first.
1046        c allButFirstDo:
1047          [| :each | result := binBlock apply*, result, each].
1048        result]
1051 c@(Sequence traits) reverseAllButLastDo: block
1052 "allButLastDo: in reversed order"
1054   c indexLast - 1 downTo: c indexFirst do:
1055     [| :index | block apply*, (c at: index)].
1058 c@(Sequence traits) reverseReduce: binBlock ifEmpty: emptyBlock
1059 "Like foldr in Haskell or Sequence reversed reduce:ifEmpty:"
1060 [| result |
1061   c isEmpty
1062     ifTrue: [emptyBlock do]
1063     ifFalse:
1064       [result := c last.
1065        c reverseAllButLastDo:
1066          [| :each | result := binBlock apply*, result, each].
1067        result]
1070 c@(Sequence traits) reverseReduce: binBlock
1071 "Like foldr in Haskell or Sequence reversed reduce:"
1073   c reverseReduce: binBlock ifEmpty: []
1076 prefix@(Sequence traits) isPrefixOf: seq@(Sequence traits)
1077 "Answer whether the first elements of the Sequence match the potential prefix
1078 Sequence's elements in order."
1080   prefix size <= seq size
1081     /\ [prefix doWithIndex: [| :each :index |
1082           each = (seq at: index) ifFalse: [^ False]].
1083         True]
1086 suffix@(Sequence traits) isSuffixOf: seq@(Sequence traits)
1087 "Answer whether the last elements of the Sequence match the potential suffix
1088 Sequence's elements in order."
1089 [| seqIndex |
1090   seq size >= suffix size /\
1091     [seqIndex := seq indexLast.
1092      suffix reverseDoWithIndex:
1093       [| :each :index |
1094        each = (seq at: seqIndex) ifFalse: [^ False].
1095        seqIndex -= 1].
1096      True]
1099 c@(Sequence traits) reverse
1100 "Reverse the element order of the Sequence in place."
1102   0 to: c indexLast `cache // 2 do:
1103     [| :index | c swap: index with: c indexLast - index].
1104   c
1107 c@(Sequence traits) reversed
1108 "Answer a new Sequence with the element order reversed."
1109 [| srcIdx |
1110   result ::= c new &capacity: c size `cache.
1111   srcIdx := c size.
1112   c keysDo: [| :destIdx | result at: destIdx put: (c at: (srcIdx -= 1))].
1113   result
1116 a@(Sequence traits) isSortedBy: block
1117 "Whether the elements in the Sequence are arranged in order by the block
1118 comparison."
1119 [| lastObj obj |
1120   a isEmpty ifTrue: [^ True].
1121   lastObj := a first.
1122   1 below: a size do: [| :index |
1123     obj := a at: index.
1124     (block apply*, lastObj, obj) ifFalse: [^ False].
1125     lastObj := obj].
1126   True
1129 s@(Sequence traits) sortFrom: start to: end by: block
1130 "Perform a quick-sort within the start/end bounds using the sort block."
1131 [| low high pivot |
1132   start >= end ifTrue: [^ s].
1133   low := start + 1.
1134   high := end.
1135   pivot := s at: start.
1136   [low <= high]
1137     whileTrue:
1138       [[low <= high /\ [block apply*, (s at: low), pivot]]
1139         whileTrue: [low += 1].
1140         [low <= high /\ [block apply*, pivot, (s at: high)]]
1141           whileTrue: [high -= 1].
1142         low < high
1143           ifTrue: [s swap: low with: high]].
1144   low -= 1.
1145   low > start
1146     ifTrue:
1147       [s swap: start with: low.
1148        s sortFrom: start to: low - 1 by: block].
1149   low < end
1150     ifTrue:
1151       [s sortFrom: low + 1 to: end by: block].
1152   s
1155 a@(Sequence traits) destructiveSortBy: block
1156 "Sort the elements by a binary block comparison. Perform a quick-sort by
1157 default. The sense of the block is that it should return the equivalent of
1158 <=: whether the first element not greater than the second."
1160   a sortFrom: 0 to: a indexLast by: block
1163 a@(Sequence traits) destructiveSort &comparison: block
1164 "The default comparison given to sort the Sequence's elements is <=."
1166   a destructiveSortBy: (block ifNil: [#<=`er])
1169 s@(Sequence traits) stableSortFrom: start to: end by: block
1170 "Perform a stable sort (using merge-sort algorithm) within the
1171 start/end bounds using the sort block."
1172 [| middle scopy i1 i2 val1 val2 out |
1173   s size <= 1 ifTrue: [^ s].
1174   end > s size ifTrue: [end := s size].
1175   start >= end ifTrue: [^ s].
1176   middle := (start + end) // 2.
1177   scopy := s copy.
1178   scopy stableSortFrom: start to: middle by: block.
1179   scopy stableSortFrom: middle + 1 to: end by: block.
1180   val1 := scopy at: (i1 := start).
1181   val2 := scopy at: (i2 := middle + 1).
1182   out := start - 1.
1183   [(i1 <= middle) /\ [i2 <= end]] whileTrue:
1184     [(block apply*, val1, val2)
1185        ifTrue: [s at: (out += 1) put: val1.
1186                 val1 := scopy at: (i1 += 1)]
1187        ifFalse: [s at: (out += 1) put: val2.
1188                  (i2 += 1) <= end ifTrue: [val2 := scopy at: i2]]].
1189   i1 <= middle
1190     ifTrue:  [s replaceFrom: out + 1 to: end with: scopy startingAt: i1]
1191     ifFalse: [s replaceFrom: out + 1 to: end with: scopy startingAt: i2]
1194 s@(Sequence traits) destructiveStableSortBy: block
1196   s stableSortFrom: 0 to: s indexLast by: block
1199 s@(Sequence traits) destructiveStableSort &comparison: block
1201   s destructiveStableSortBy: (block ifNil: [#<=`er])
1204 s@(Sequence traits) sortBy: sortBlock attributeBlock: slowBlock
1205 "This applies the 'Schwartzian Transform', suitable for when a sort needs
1206 to be performed by comparing the values of an expensive calculation on the
1207 values. By caching the results of the calculation using Associations,
1208 the use of the block is limited to O(N). An intermediate Sequence is
1209 used."
1211   ((s collect: [| :each | each -> (slowBlock apply*, each)])
1212     destructiveSortBy: [| :a :b | sortBlock apply*, a value, b value])
1213    collect: #key`er
1216 x caseOf: cases@(Sequence traits) otherwise: block
1217 "A Sequence of Associations between Objects and Blocks is used as a control
1218 structure. Matching against the first argument causes the respective block
1219 to be evaluated and its result returned. If none match, an alternative block
1220 is provided which then is evaluated for an answer."
1222   cases do: [| :assoc | assoc key = x ifTrue: [^ assoc value do]].
1223   block do
1226 x caseOf: cases@(Sequence traits)
1227 "Executes the case-switching logic with a null alternative block."
1228 [x caseOf: cases otherwise: []].
1230 x conditions: conds@(Sequence traits) otherwise: block
1231 "A Sequence of Associations between Blocks is used as a control structure.
1232 Each key is evaluated and if it answers True, the associated block is evaluated
1233 and its answer passed along, and evaluation stops. If none are true, an
1234 alternative block is provided which then is evaluated for an answer."
1236   conds do: [| :assoc | assoc key do ifTrue: [^ assoc value do]].
1237   block do
1240 x conditions: conds@(Sequence traits)
1241 "Executes the condition-switching logic with a null alternative block."
1242 [x conditions: conds otherwise: []].
1244 c@(Sequence traits) collect: block into: seq@(Sequence traits)
1245 "Specialized to handle sequences which are not Extensible. This returns a
1246 result of applying the block to each element, which may not be the given
1247 Sequence if it was not of the right size."
1249   c collectWithIndex: [| :each :_ | block apply*, each]
1250     into: (seq capacity = c size ifTrue: [seq] ifFalse: [seq newSizeOf: c])
1253 s@(Sequence traits) splitIntoSize: n
1254 "Answers the result of splitting the sequence into n-sized sequences.
1255 If size of the sequence is not divisible by n, the last element will be
1256 smaller."
1258    `conditions: (
1259      [n <= 0] -> [error: 'Split size must be positive.'].
1260      [n >= s size] -> [{s copy}]
1261    ) otherwise:
1262      [| subSeqs sepIndex |
1263       subSeqs := (Array newSize: s size // n + 1) writer.
1264       sepIndex := 0.
1265       [sepIndex < (s size - (s size mod: n))]
1266         whileTrue: [subSeqs nextPut: (s copyFrom: sepIndex to: sepIndex + n - 1).
1267                     sepIndex += n].
1268       sepIndex = s size "There are extra elements; a remainder to be added."
1269         ifFalse: [subSeqs nextPut: (s copyFrom: sepIndex to: s indexLast)].
1270       subSeqs contents]
1273 x@(Sequence traits) beginsWith: y@(Sequence traits)
1274 "Answer whether the sequence begins with another sequence."
1276   x size >= y size /\
1277    [x with: y do: [| :each1 :each2 | each1 = each2 ifFalse: [^ False]]. True]
1280 x@(Sequence traits) endsWith: y@(Sequence traits)
1281 "Answer whether the sequence ends with another sequence."
1283   x size >= y size /\
1284    [x reverseWith: y do: [| :each1 :each2 | each1 = each2 ifFalse: [^ False]]. True]
1287 x@(Sequence traits) truncateTo: limit paddedBy: padElement &onRight: onRight
1289   x size <= limit
1290     ifTrue: [(onRight `defaultsTo: False)
1291                ifTrue: [x ; (padElement repeatedTimes: limit - x size)]
1292                ifFalse: [x new ; (padElement repeatedTimes: limit - x size) ; x]]
1293     ifFalse: [x sliceUpTo: limit]
1296 x@(Sequence traits) truncateTo: limit
1297 "Return a new Sequence no longer than the limit or the sequence itself if it is
1298 shorter than the limit."
1300   limit < x size
1301     ifTrue: [x sliceUpTo: limit]
1302     ifFalse: [x]