2 collections define: #Range &parents: {Sequence} &slots: {
3 #start -> 0. "The number to start from; this is the first value."
4 #end -> PositiveInfinity. "The number to end with; this may not be the last value, but no number in the Range will exceed it."
5 #step -> 1 "The interval of stepping. Each successive element will exceed the preceding by exactly this amount."}.
6 "A Sequence of Numbers with a regular interval and bounding values."
8 "TODO: find some way to parametrize this on all Comparable types and ensure
9 that all three parameters of the Range are comparable."
11 r@(Range traits) new &capacity: n
12 "Default to a reasonably useful concrete type to make a new Sequence."
14 r arrayType new &capacity: n
17 r@(Range traits) newFrom: start to: end by: step
18 [r cloneSettingSlots: #{#start. #end. #step} to: {start. end. step}].
20 r@(Range traits) newFrom: start to: end
21 "This method implicitly defines a sensible default stepping value."
23 (step := end <=> start)
24 isZero ifTrue: [step := 1].
25 r newFrom: start to: end by: step
28 s@(Sequence traits) as: r@(Range traits)
30 s size `cache caseOf: {
31 0 -> [r newFrom: 0 to: 0 by: 0].
32 1 -> [r newFrom: s first to: s last]
34 [(result ::= r newFrom: s first to: s last by: s last - s first // (s size - 1)) = s
36 ifFalse: [error: 'The Sequence is not an arithmetic progression.']]
39 start@(Number traits) to: end
41 Range newFrom: start to: end
44 start@(Number traits) to: end by: step
46 Range newFrom: start to: end by: step
49 start@(Number traits) upTo: end
51 Range newFrom: start to: end
54 start@(Number traits) below: end
56 Range newFrom: start to: end - 1
59 start@(Number traits) downTo: end
61 Range newFrom: start to: end
64 start@(Number traits) above: end
66 Range newFrom: start to: end + 1
69 start@(Number traits) upTo: end by: step
71 Range newFrom: start to: end by: step
74 start@(Number traits) below: end by: step
76 Range newFrom: start to: end - 1 by: step
79 start@(Number traits) downTo: end by: step
81 Range newFrom: start to: end by: step
84 start@(Number traits) above: end by: step
86 Range newFrom: start to: end + 1 by: step
89 start@(Number traits) andAbove
91 Range newFrom: start to: PositiveInfinity
94 end@(Number traits) andBelow
96 Range newFrom: NegativeInfinity to: end
99 start@(Number traits) andAboveBy: step
101 Range newFrom: start to: PositiveInfinity by: step
104 end@(Number traits) andBelowBy: step
106 Range newFrom: NegativeInfinity to: end by: step
109 r1@(Range traits) = r2@(Range traits)
112 /\ [r1 step = r2 step]
113 /\ [r1 size = r2 size]
116 r@(Range traits) hash
118 (r start hash bitShift: 2)
124 r@(Range traits) copy
126 r newFrom: r start to: r end by: r step
129 r@(Range traits) at: n@(Integer traits)
134 [n isPositive /\ [n < r size]
135 ifTrue: [r step * n + r start]
136 ifFalse: [n keyNotFoundOn: r]]
139 r@(Range traits) at: n put: _
141 error: 'Ranges are not mutable.'
144 r@(Range traits) extent
145 "Answers the magnitude of the range's extent."
150 r@(Range traits) first
155 r@(Range traits) last
157 r end - (r end - r start \\ r step)
160 r@(Range traits) rangeIncludes: n
163 ifTrue: [n between: r end and: r start]
164 ifFalse: [n between: r start and: r end]
167 r@(Range traits) includes: n
168 "Answer whether the number is part of the interval defined by the boundaries,
169 and then whether it is calculably close to an integer step from the start."
172 /\ [((n - r start as: Float) / r step) fractionPart isCloseTo: 0.0]
175 r@(Range traits) isInfinite
176 "Whether the Range includes an infinite number of elements."
177 "TODO: guard iteration protocols on this condition."
179 r start isInfinite \/ [r end isInfinite]
182 r@(Range traits) size
183 "Check the 'direction' of the Range to make sure that it's possible to have
187 ifTrue: [PositiveInfinity]
188 ifFalse: [(r step isNegative xor: r start > r end)
190 ifFalse: [r end - r start // r step + 1]]
193 r@(Range traits) collect: block
194 "Ensure that an Array is produced of the right size."
196 result ::= r arrayType newSizeOf: r.
200 result at: i put: (block applyWith: each).
205 r@(Range traits) project: block
206 "Answer an Array instead of a Dictionary to map the values."
207 [r project: block into: (Array newSizeOf: r)].
209 r@(Range traits) do: block
213 ifTrue: [[r end <= each]
214 whileTrue: [block applyWith: each. each += r step]]
215 ifFalse: [[r end >= each]
216 whileTrue: [block applyWith: each. each += r step]].
220 r@(Range traits) reverseDo: block
224 ifTrue: [[r end >= each]
225 whileTrue: [block applyWith: each. each -= step]]
226 ifFalse: [[r end <= each]
227 whileTrue: [block applyWith: each. each -= step]].
231 r1@(Range traits) intersects: r2@(Range traits)
232 "Whether the Ranges have overlap in their extents."
234 (r1 start between: r2 start and: r2 end)
235 \/ [r2 start between: r1 start and: r1 end]
238 r1@(Range traits) /\ r2@(Range traits)
239 "Returns the intersection of the two Ranges, or Nil if none."
244 ifTrue: [(r1 start max: r2 start) to: (r1 end min: r2 end) by: r1 step]
246 [({r1 step. r2 step} includes: (r1 step gcd: r2 step))
247 ifTrue: [(r1 start max: r2 start) to: (r1 end min: r2 end) by: (r1 step min: r2 step)]
248 ifFalse: ["Ranges do not step harmonically."]]]
251 "Some very basic range math"
253 r@(Range traits) + n@(Number traits)
256 r start + n to: r end + n by: r step
259 n@(Number traits) + r@(Range traits)
264 r@(Range traits) - n@(Number traits)
269 collections define: #LogicRange &parents: {Range}.
270 "A Range which uses blocks for the end and step aspects."
271 LogicRange start := 0.
272 "The starting value."
273 LogicRange end := [| :x | x > PositiveInfinity].
274 "A Block which should halt iteration when reaching True."
275 LogicRange step := [| :x | x + 1].
276 "A Block returning the next value in the Sequence based on the current one."
278 r@(LogicRange traits) newFrom: start to: end by: step
279 "Coerces any numerical parameters into blocks where necessary."
281 goesUp ::= (step is: Number)
282 ifTrue: [step isPositive]
283 ifFalse: [(step applyWith: start) > start].
284 r cloneSettingSlots: #{#start. #end. #step}
288 ifTrue: [[| :x | x >= end]]
289 ifFalse: [[| :x | x <= end]]]
293 ifTrue: [[| :x | x + step]]
294 ifFalse: [[| :x | x - step]]]
298 start@(Number traits) until: end by: step
299 "Creates a new LogicRange."
301 LogicRange newFrom: start to: end by: step
304 start@(Number traits) by: step
305 "Creates a new LogicRange with the default end."
307 LogicRange newFrom: start to: LogicRange end by: step
310 start@(Number traits) until: end
311 "Creates a new LogicRange with the default step."
313 LogicRange newFrom: start to: end by: LogicRange step
316 r@(LogicRange traits) isEmpty
318 r do: [| :_ | ^ False].
322 r@(LogicRange traits) size
323 "This should obviously be avoided."
325 r end == LogicRange end
326 ifTrue: [PositiveInfinity]
327 ifFalse: [r inject: 0 into: [| :size :_ | size + 1]]
330 r@(LogicRange traits) at: n
335 result := r step applyWith: result.
336 (r end applyWith: result) ifTrue: [error: 'Subscript out of bounds.']].
340 r@(LogicRange traits) do: block
341 "Iterates through values from the start until the end-test is satisfied."
344 [r end applyWith: each]
345 whileFalse: [block applyWith: each.
346 each := r step applyWith: each].
350 r@(LogicRange traits) reverseDo: block
351 "Since the end is defined by a test and not a value, this is ambiguous."
356 r@(LogicRange traits) isAscending
357 "Whether the Range's progression is positive."
359 (r step applyWith: r start) > r start
362 r@(LogicRange traits) collect: block
363 "Collect the Range's elements into a WriteStream of the arrayType."
366 result := r arrayType writer.
367 r do: [| :each | result nextPut: (block applyWith: each)]
368 ] writingAs: r arrayType
371 r@(LogicRange traits) rangeIncludes: n
372 "Whether the value is in the direction of the Range and between the start
373 while not exceeding the end test."
375 (r end applyWith: n) not
377 ifTrue: [n >= r start]
378 ifFalse: [n <= r start]]
381 r@(LogicRange traits) includes: n
382 "Simply iterate through the values and compare."
384 r do: [| :each | n = each ifTrue: [^ True]].