Foreach Range Statement

Don Clugston dac at nospam.com.au
Tue Jul 24 00:07:56 PDT 2007


Bill Baxter wrote:
> Don Clugston wrote:
>> Bill Baxter wrote:
>>> Don Clugston wrote:
>>>> Reiner Pope wrote:
>>>>> Bill Baxter wrote:
>>>>>> Jarrett Billingsley wrote:
>>>>>>> "Xinok" <xnknet at gmail.com> wrote in message 
>>>>>>> news:f80qof$2n0l$1 at digitalmars.com...
>>>>>>>
>>>>>>>> foreach(i; 0..100)
>>>>>>>
>>>>>>> This is almost identical to the syntax in MiniD:
>>>>>>>
>>>>>>> for(i: 0 .. 100)
>>>>>>>
>>>>>>> It could be done with for or foreach; I just chose for because 
>>>>>>> normally you use for loops to iterate over ranges of integers.
>>>>>>>
>>>>>>> You can also come up with a pretty simple short-term solution 
>>>>>>> that'll be fairly efficient (though not as efficient as if the 
>>>>>>> compiler were aware of this kind of loop intrinsically) by making 
>>>>>>> a struct 'range' which has a static opCall to construct a range 
>>>>>>> and an opApply to iterate over the values, so that it'd look like:
>>>>>>>
>>>>>>> foreach(i; range(100))
>>>>>>>
>>>>>>> Which isn't terrible at all. 
>>>>>>
>>>>>> And it has the advantage of being more extensible.  And for 
>>>>>> allowing ranges to be treated as first class entities that can be 
>>>>>> passed around and manipulated.  But no, instead we get another 
>>>>>> one-trick pony.
>>>>>>
>>>>>> --bb
>>>>> That was my first thought, too.
>>>>>
>>>>> In the "Array Slice Ranges" thread, several people mentioned 
>>>>> first-class ranges:
>>>>>
>>>>> http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=43865 
>>>>>
>>>>> http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=43904 
>>>>>
>>>>> http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=43905 
>>>>>
>>>>> http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=43954 
>>>>>
>>>>>
>>>>> Your implementation, Bill, seems to be just right, and gives you 
>>>>> foreach over ranges for free.
>>>>>
>>>>> What's wrong with adding that to the language, but templated and 
>>>>> with nice syntax?
>>>>>
>>>>> type name                                 literal
>>>>> int..int  (range of int)                  1..5
>>>>> int..double   (range of int to double)    1..5.0
>>>>> int..int:int  (stepped range)             5..1:-1
>>>>>
>>>>> (I'm not sure of the use of mixed-type ranges, but this seems the 
>>>>> most intuitive syntax. Since most ranges are probably of one type, 
>>>>> how about allowing a symbol to denote "same type again". Any of the 
>>>>> following could mean int..int:   int..#,   int.._, int..$)
>>>>
>>>> I don't think it make sense to have mixed type ranges. The normal 
>>>> promotion rules should apply. However...
>>>>
>>>> Floating-point ranges are tricky. Should they be open-ended, or 
>>>> closed-ended? 
>>>
>>> Both Matlab and Numpy have floating point ranges.   Matlab ranges are 
>>> always inclusive, so 1:2.1:7.3 gives you 1.0, 3.1, 5.2, 7.3.  Python 
>>> ranges are always non-inclusive, so it gives you 1.0,3.1,5.2.
>>>
>>>> Consider
>>>> -real.infinity..real.infinity
>>>> Are the infinities part of the range? If not, how do you specify a 
>>>> range which includes infinity?
>>>
>>> Does it matter that much?  I suppose it would be cool if it did 
>>> something really consistent, but Numpy just craps out and gives you 
>>> an empty list, and Matlab raises an error "Maximum variable size 
>>> allowed by the program is exceeded".
>>
>> I think that if you can't specify a range including an infinity, then 
>> floating point ranges don't make sense. Especially, I really don't 
>> like the idea that -real.infinity..real.infinity would include 
>> -infinity, but not +infinity.
>  > I've had a use for floating-point ranges: specifying domain and range of
>  > functions, where infinity is fairly common. When else would you use 
> them?
>  >
> 
> It sounds like maybe you're talking about "intervals" rather than 
> "ranges".  Yes, definitely intervals should be able to handle infinities 
> correctly.  But a range (a la python or matlab) is a shortcut for a 
> sequence of values, with equal-size steps in between beginning and end. 

OK, that makes sense. Although, for the integer case it's clear how many 
elements are in a range; it's not at all obvious for floating point.

>  To have the beginning or end be infinite is asking for trouble.  For 
> instance in Matlab that tries to allocate an infinite-sized array of 
> numbers.

Agreed. Although 0..infinity is only one element bigger than 0..real.max.

> 
>> Besides, "first_element .. last_element-1" doesn't work for (say)
>> 0.00001 .. 0.00003;
> 
> Sure it does.  It's just a set containing only 0.00001.  I don't know 
> what you mean by -1 there.

I mean, in the floating point case, you can't work out the last element of the 
range by subtracting 1 from it.


>> But we probably don't want 1.0..5.0 to contain 5.0 when 1..5 doesn't 
>> contain 5.
> 
> Right.  Numpy had the same problem.  Python itself uses the same 
> non-inclusive rule as D. But Python only handles integers in things like 
> the "range(start,end,step)" function.  The Numpy folks wanted to extend 
> that to work for floating point types as well.  But actually, in both 
> matlab and numpy, if you want an evenly spaced set of numbers, you 
> usually use the 'linspace' function, which has the signature 
> linspace(begin,end,numvals).  This creates an inclusive array of numbers.

That makes sense. You could also have a logarithmic range.
But what need is there for using ".." with floating point numbers? Surely we can 
already write
foreach(float x, linspace(begin, end, numvals)){}

> I think one source of confusion is that ranges and slices are very 
> similar things, but not quite the same.
> 
> * A range is just a sequence of numbers.  It can exist and be 
> interpreted independently.  Here allowing floating point numbers makes 
> sense.  Allowing for infinity may make sense, but practically it's very 
> niche.  Iterating over infinite things usually takes either too much 
> time or too much memory.

How is that different to a set? I've always assumed a range (a,b) contained 
EVERYTHING between a and b. I'm not very familiar with either Python or Matlab.

> * A slice needs an object to operate on for interpretation of 
> object-relative things like $.  Generally speaking, only integers make 
> sense in a slice.  Infinity doesn't really make sense because you can't 
> generally have things that are both slice-able and infinite on a computer.
> 
> (* An interval just represents two points on a numberline, plus maybe an 
> indication of the inclusivity of the endpoints.  Infinity -- ok. 
> Floating point -- ok.)
> 
> It may be possible to combine the concepts into one type, but they *are* 
> slightly different, and may benefit from being treated as so.

That clarification is very helpful. Thanks.



More information about the Digitalmars-d mailing list