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
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: _
16 s@(Sequence traits) keySet
17 "Answers the keys of a Sequence as a Range; will not work until Range is
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].
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)
63 result ::= s newSizeOf: c.
65 c do: [| :each | result at: index put: each. index += 1].
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)
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)
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."
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."
106 c@(Sequence traits) after: obj ifAbsent: block
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
122 ifTrue: [c copyFrom: n to: c indexLast]
126 c@(Sequence traits) allButFirst
131 c@(Sequence traits) allButLast: n
134 ifTrue: [c copyFrom: 0 to: c size - n - 1]
138 c@(Sequence traits) allButLast
143 c@(Sequence traits) anyOne
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."
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]
171 c@(Sequence traits) atAll: d@(Collection traits)
172 "Returns a new Sequence of the elements corresponding to the indexes in the
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
182 (result ::= c newSizeOf: d) keysDo:
184 result at: index put: (c at: (d at: index))].
188 c@(Sequence traits) atAll: d put: obj
189 "Replace the element at each index with the corresponding value of the
192 d do: #(c at: _ put: obj) `er.
196 c@(Sequence traits) atAll: indices@(Sequence traits) put: values
197 "Replace the element at each index with the corresponding value of the
200 indices with: values do: #(c at: _ put: _) `er.
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."
221 \/ [c size `cache = d size
222 /\ [0 below: c size do:
223 [| :index | (c at: index) = (d at: index) ifFalse: [^ False]].
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
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]].
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]].
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."
309 result ::= ExtensibleArray new.
311 [(position := c indexOfFirstSatisfying: block startingAt: position) isNil]
312 whileFalse: [result addLast: position. position += 1].
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
331 [subSeq isEmpty] -> [block do].
332 [subSeq size = 1] -> [c indexOf: subSeq first startingAt: start ifAbsent: block]
335 first := subSeq first.
336 start upTo: c size - subSeq size do: [| :startIndex |
337 (c at: startIndex) = first ifTrue:
339 [(c at: startIndex + index) = (subSeq at: index)]
340 whileTrue: [(index += 1) = subSeq size ifTrue: [^ startIndex]]]].
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]].
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]]].
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."
399 [| :index | delims do:
400 [| :obj | obj = (s at: index) ifTrue: [^ index]]].
404 c@(Sequence traits) replaceAll: obj with: newOne
405 "Replace all elements equal to the given object with the replacement given."
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.
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)."
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).
477 [result replaceFrom: targetIndex below: c size with: c.
479 result replaceFrom: targetIndex below: targetIndex + delim size with: delim.
480 targetIndex += delim size].
483 eachSize := each size.
484 result replaceFrom: targetIndex below: targetIndex + eachSize with: each.
485 targetIndex += eachSize]
487 [result replaceFrom: targetIndex below: targetIndex + delim size with: delim.
488 targetIndex += delim size].
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:
503 result at: index + srcIndex put: each]].
507 c@(Sequence traits) copyAfter: obj
508 "Answer a similar Sequence of all but the elements before the first matching
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
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
530 end < start \/ [c isEmpty]
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
544 resultSize ::= c size + d size `cache - (end - start + 1).
545 endReplace ::= start - 1 + d size.
546 result ::= c new &capacity: resultSize.
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].
556 c@(Sequence traits) copyReplaceAll: seq with: replacement
557 "Answer a copy with any occurrences of the (sub)sequence replaced with the
559 [| start result currentIndex end |
562 [(currentIndex := result indexOfSubSeq: seq startingAt: start) isNotNil]
564 [end := currentIndex + seq indexLast.
565 result := result copyReplaceFrom: currentIndex to: end with: replacement.
566 start := currentIndex + replacement size].
570 c@(Sequence traits) copyReplace: element with: obj
571 "Answer a copy with any occurrences of the element replaced with the given
574 result ::= c newSameSize.
577 result at: index put: (each = element ifTrue: [obj] ifFalse: [each])].
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.
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.
605 replaceFrom: index + 1 to: cs with: c startingAt: index. ]
608 c@(Sequence traits) copyWithoutFirst
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]].
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:
637 delims detect: [| :c | c = (s at: index)]
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:
650 [| :delim | delim = (s at: index) ifTrue: [^ index]]].
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."
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).
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)]].
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."
677 subSeqs ::= ExtensibleArray new.
679 [keyEnd < s size] whileTrue:
680 [count isNotNil /\ [subSeqs size >= count]
681 ifTrue: [subSeqs add: (s copyFrom: keyEnd to: s indexLast).
683 keyStart := s indexPastAll: delims startingAt: keyEnd.
684 keyEnd := s indexOfAny: delims startingAt: keyStart.
686 ifTrue: [subSeqs add:
687 (s copyFrom: keyStart to: keyEnd - 1)]].
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."
702 result ::= s new &capacity: n.
703 result keysDo: [| :index | result at: index put: (s at: index // s size)].
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
714 [k isZero] -> [block apply*, workingArray]
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
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
736 end < start \/ [c isEmpty]
739 [result ::= c new &capacity: end - start + 1.
741 result keysDo: [| :index | result at: index put: (block apply*, (c at: j)). j += 1].
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
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."
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."
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."
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."
833 [| :each :index | seq at: index put: (binBlock apply*, each, index)].
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."
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
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.
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:
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."
941 elems ::= c copyFrom: index below: index + items.
942 steps isNegative ifTrue:
943 [steps := steps abs. index += items - 1. items := items negated].
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 ×: 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)."
961 count `defaultsTo: 1.
962 count := count mod: c size.
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 ×: 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)."
974 count `defaultsTo: 1.
975 c size isZero \/ count isZero
978 [count := count mod: c size.
979 index := c size - count mod: c size.
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))].
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
1014 [| :each | result := binBlock apply*, result, each]]
1017 c do: [| :each | result := binBlock apply*, result, each]].
1021 c@(Sequence traits) reverseGather: binBlock &initial: init
1029 c reverseAllButLastDo:
1030 [| :each | result := binBlock apply*, result, each]]
1033 c reverseDo: [| :each | result := binBlock apply*, result, each]].
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."
1043 ifTrue: [emptyBlock do]
1047 [| :each | result := binBlock apply*, result, each].
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:"
1062 ifTrue: [emptyBlock do]
1065 c reverseAllButLastDo:
1066 [| :each | result := binBlock apply*, result, each].
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]].
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."
1090 seq size >= suffix size /\
1091 [seqIndex := seq indexLast.
1092 suffix reverseDoWithIndex:
1094 each = (seq at: seqIndex) ifFalse: [^ False].
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].
1107 c@(Sequence traits) reversed
1108 "Answer a new Sequence with the element order reversed."
1110 result ::= c new &capacity: c size `cache.
1112 c keysDo: [| :destIdx | result at: destIdx put: (c at: (srcIdx -= 1))].
1116 a@(Sequence traits) isSortedBy: block
1117 "Whether the elements in the Sequence are arranged in order by the block
1120 a isEmpty ifTrue: [^ True].
1122 1 below: a size do: [| :index |
1124 (block apply*, lastObj, obj) ifFalse: [^ False].
1129 s@(Sequence traits) sortFrom: start to: end by: block
1130 "Perform a quick-sort within the start/end bounds using the sort block."
1132 start >= end ifTrue: [^ s].
1135 pivot := s at: start.
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].
1143 ifTrue: [s swap: low with: high]].
1147 [s swap: start with: low.
1148 s sortFrom: start to: low - 1 by: block].
1151 [s sortFrom: low + 1 to: end by: block].
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.
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).
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]]].
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
1211 ((s collect: [| :each | each -> (slowBlock apply*, each)])
1212 destructiveSortBy: [| :a :b | sortBlock apply*, a value, b value])
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]].
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]].
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
1259 [n <= 0] -> [error: 'Split size must be positive.'].
1260 [n >= s size] -> [{s copy}]
1262 [| subSeqs sepIndex |
1263 subSeqs := (Array newSize: s size // n + 1) writer.
1265 [sepIndex < (s size - (s size mod: n))]
1266 whileTrue: [subSeqs nextPut: (s copyFrom: sepIndex to: sepIndex + n - 1).
1268 sepIndex = s size "There are extra elements; a remainder to be added."
1269 ifFalse: [subSeqs nextPut: (s copyFrom: sepIndex to: s indexLast)].
1273 x@(Sequence traits) beginsWith: y@(Sequence traits)
1274 "Answer whether the sequence begins with another sequence."
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."
1284 [x reverseWith: y do: [| :each1 :each2 | each1 = each2 ifFalse: [^ False]]. True]
1287 x@(Sequence traits) truncateTo: limit paddedBy: padElement &onRight: onRight
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."
1301 ifTrue: [x sliceUpTo: limit]