[swift-evolution] Feature proposal: Range operator with step

Ted F.A. van Gaalen tedvgiosdev at gmail.com
Wed Apr 6 15:08:59 CDT 2016

Hi Erica, Dave

Based on what I’ve (not all) read under this topic: 

I’d suggest a more complete approach:

Ranges should support:  (as yet not implemented) 

- All numerical data types (Double, Float, Int, Money***, Decimal*** ) 
- An arbitrary increment or decrement value.
- Working in the complete  - + numerical range
- Allow traversing in both   - + directions.

The basics:

    (v1…v2).by(v3)     // this still is readable. and can be optimized by the compiler (predictable sequence)    


    v1, v2, v3  are any numerical type scalar type  
    v1, v2, v3  must have the same numerical type.
    v1 >  v2:   is allowed and correctly evaluated.  e.g. (8.0…-3.14159).by(-0.0001) 

    The  ..<  half open operator is no longer allowed.   write e.g.   0...ar.count - 1

    "by(…)”  is obligatory with floating point range.

           the default “by(…)” value of 1 makes sense only with integer ranges.

valid examples:
    (5…9)                        // 5 6 7 8 9 Integer range without “by” defaults to 1 as increment value.
    (1...10).by(2)             // 1 3 5 7 9.
    (2...10).by(2)             // 2 4 6 8 10.
    (4…-4).by(2)             // 4  2 0 -2 -4 .        // runs backwards
    (30..-10).by(-2)        // 30 28 26 24 22 ….. -10.    
    (10...0).by(-3)          // 10 7 4 1.                 

    (12…-10)                 // is valid, but returns empty because default increment value = 1

    (12.0…-12.0).by(-1.5)       // 12.0  10.5  9.0….          // of course with float imprecision

   invalid examples:

   (23.0..<60.5).by(0.5)         // half open ranges are no  longer allowed ** 
  (23.0…60.5)                     // “ by"  is obligatory with floats.
  (14...8).by(0.5)                //  v1 v2 and v3 don’t have the same numerical type 

Half open ranges make no real sense (are not really useful) with floating point values.
and no sense at all with e.g (12..<-1)  afaics 

At least for float iterations the increment value should not each time be accumulated like
v1 += v3;  v1 += v3;  v1 += v3;  ….                       // causes float number drift.
but be freshly multiplied with each iteration, e.g. by using an internal iteration counter
like so:

v1 = v3 * i++;   v1 = v3 * i++;   v1 = v3 * i++;….

for reasons of precision.

If one has worked with floating point data more often
awareness of its precision limitations become a second nature conscience. 
E.g. it is perfectly acceptable and known (also in classical for-loops) that
(0.0…1.0).by(0.1) is not guaranteed to reach the humanly expected value of 1.0.
This is normal. Due to the nature of what mostly is done in the
floating point numbers domain, this does not often cause a problem
(e.g like working with a slide ruler) 
if it is important to reach the “expected end” then one could
-add an epsilon value like so (v1…v2 + 0.1) 
-generate the desired float sequence within an integer loop.

The above “range” (imho) improvement makes the 
stride.. function/keyword completely unnecessary.

Due to its ability to process reversed ranges as is, 
the .reverse() is optional (no longer necessary in most cases,
allowing the compiler to optimize without having to process 
it like a collection.

Now that we have a fully functional range, which can do all things desired, one can
then of course, pass this range without any change  to a collection based for …  e.g.

for v in (v1…v2).by(v3)   // optionally add other collection operators/filters/sorts here

(or in any other construct you might see fit)

This seems to be a reasonable alternative for

- the classical for ;; loop
-the collection-free for-loop  
     for v from v1 to v2 by v3

As you know, the latter is what I have been suggesting, 
but seemingly does not find much support,
(because I received very little reactions) 
making it doubtful if I will ever make a proposal for this for loop.
Anyone out there should I stil do that? 

When one does not further extend the range
with filters like sort etc. the compiler can still optimize 
a collection-in-between out of the way.
(Thank you Taras, für das Mitdenken. :o)

**   note that a half open range would in most cases be unnecessary 
      if  collection indexes started with 1 instead of 0, e.g. someArray[1…10] 
     (but it’s too late to change that.) 
     “the color of the first apple?”    vs
     “the color of the zeroth (0) ???  apple?”    ? Silly isn’t? 

*** possible future numerical “native” types 

It could be that (at least) partly something similar has already been suggested.
but so much is here of this topic that i can’t see the wood for the trees, so to speak.

Your (all) opinions are appreciated. 

kind regards, mit freundlichen Grüssen, Met vriendelijke groeten, 
Sigh, why do we Dutch always have to speak the languages of the bigger countries :o) 

> on Wed Apr 06 2016, Erica Sadun <erica-AT-ericasadun.com <http://erica-at-ericasadun.com/>> wrote:
>>    On Apr 6, 2016, at 12:16 PM, Dave Abrahams via swift-evolution
>>    <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>    (0..<199).striding(by: -2)
>>    are even or odd.
>> (0..<199).striding(by: -2): 0..<199 == 0...198 Even
>> (1..<199).striding(by: -2): 1..<199 == 1...198 Even
> I understand the logic that got you there, but I find it incredibly
> counter-intuitive that striding by 2s over a range with odd endpoints
> should produce even numbers... I can't imagine any way I'd be convinced
> that was a good idea.
>> (0..<198).striding(by: -2): 1..<198 == 0...197 Odd
>> (1..<198).striding(by: -2): 1..<198 == 1...197 Odd
>> -- E

> -- 
>> Dave

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160406/83eebf3f/attachment.html>

More information about the swift-evolution mailing list