foreach enhancement

D

Daniel O'Connell [C# MVP]

The problem here is that 1...3 *would* basically be an
variable


Now I understand. Maybe you can treat x..y as a special type, because in
contrast to an IEnumerable the size of the range is known at execution
time
and has not to be enumerated first, so no temporary arraylist has to be
generated as it would otherwise be the case.
Treating it as a special type is a possibility, but I am in general hesitant
to define behaviour based on syntax instead of semantics.
But I'd rather make the assigment of all subitems to the array explicit
than
implicit,
so I vote for your "content operator", although I'd rather use # instead
of
<-.

I'm still considering this particular issue. Finding good syntax is pretty
tough. The original concept behind <- was based around another piece of
syntax which I've more or less discounted at this point.
A different, perhaps better perhaps not, solution may be to perform
automated input on expressions typed IEnumerable explicitly instead of any
other, yet I'm still not sure.
I am concerned with the # syntax due to # being used already by compiler
directives.
But for the compiler it won't make any difference wheather you have 2 or
20
ranges in the loop will it?
Outputting for each range a separate loop is the most performant solution.
The only problem is if the loop *body* is very large you really have a
code
bloat.
In that case outputting a single foreach loop which iterates over a
constructed RangeEnumerator object will be the right way.

It won't make significant difference to the compiler, but as you noted if
the code starts to bloat or conditions start to grow to much the compiler
should shift. I imagine there is a point where the enumerator is more
performant than inline for's and you would certainly not want a huge amount
of code generated due to a single expression. For starters I'll probably
only support optimizing 2 ranges and consider adding additional
optimizations if I think it makes sense. It is only a proof of concept after
all.
Since this is a proof-of-concept implementation, I am also looking at
slightly more complicated subjects like haskell&python like list
comprehensions(which I think effectivly become mini-iterators). My existing
implementatino ideas are too unpolished to post right now(6 pages of
rambling is to much), but the current syntax allows you to do things like
sequence unions and intersections, value mutation, value analysis, etc.
some rough samples:
[1...1000 where value%2 == 0] //returns all even numbers between 1 &1000,
inclusive
[yield Math.Pow(value,2) for 1...1000 where Math.Pow(value,2)%2 == 0]
//returns the square of each value which has an even square


Now you are really going to far :) Are you sure this would fit into C#?
Your first example can be solved using the optionally increment clause
and for more complicated things I would create my own enumerator.

Nope, I'm not sure about anything, ;). The entire point of this is to
experiment with ti and see how it works. List comprehensions are fairly
popular in the functional world and I am rather curious as to if they *can*
be fit into C# and if they will make life easier or not. I figure if I'm
going this far I may as well do the whole shebang and see what everyoen
thinks after they use it.
foreach (int a in GetAllPowerOfTwoWherePowerOfTwoIsEven(1, 1000))
{
list.Add(a);
}
yes and you can say foreach(int a in GetAllNumbersBetween(1,1000))
{
list.Add(a);
}

just as well. It's kind of illogical to use the argument you are fighting
for your base concept to argue down another feature, isn't it? :)
Using C#'s new anonymous methods you could do:

[delegate{foreach(int a in 1..1000){if (Math.Pow(value,2)%2 == 0)yield
return Math.Pow(value,2); }}]

I don't think you can use iterators in anonymous methods, though I could be
wrong.
Btw, a great enhancement for the ArrayList class would be an AddRange
method
and a Ctor that takes an IEnumerable as parameter which fills the list
with
all enumerated elements.

Yeah, that would make sense.
Iam very interested in it. Do you have a homepage?

No, sadly. Me and http are not particulary close friends(this is probably
due to my long running campaign to discredit http, html, and most related
technologies as garbage). As such I don't really keep web pages going too
often. I did have a blog[1] for a little while but after a few posts I
stopped using it.

Most of my ideas show up here sometime or another. I will try to writeup all
the various things I've considered *or* I will forward port all my mono
modifications and provide a complete patch containing these list features
with everything else.

1. http://www.dotnetjunkies.com/weblog/doconnell/

Maybe I'll start updating things there as work gets done on this feature.
 
D

Daniel O'Connell [C# MVP]

As you could probably tell from my previous posts, I'm all about saving
wear
and tear on the fingertips. But I also like solutions that are general
enough to be useful in a variety of situations. The operators you've come
up with, while they seem like they'll save some time and effort in very
specific circumstances, still seem to fall back to creating general
classes
that can be optimized by the compiler in very specific circumstances
(i.e.,
in looping constructs in which the range defined by 2 terminal constants).
They also seem to be very limited use -- how do you propose the creation
of,
and performing operations on, dynamic ranges at run-time?

I am considering several things, like list comprehensions, which extend the
syntax sufficently that its valid outside of foreach and isin clauses. I'm
not sure I can do it, but I'm confident I can make a good run at it.

For starters, for dynamic ranges I hope to be able to optimize fairly well.
The syntax I am considering for basic dynamic ranges will be something like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

Is that what you mean?

As far as I'm concerned code like:
GetValue()...GetOtherValue();
should form a valid range expression. Its possible the range would be
strange(25...-2) for example, where the numbers will operate in the way
numbers do on computers strictly due to the way computers work. It has a
chance for a bug, but many features do.

Also, IMHO, 1... means every number greater than one(this would stop as soon
as a number was less than one, in other words when wrapover occurs). and ...
would be effectivly an infinite sequence, or basically every number possible
for the assocaited type, starting from the minimum value of the type and
going to the max. Creating actual behaviour to that extent is pretty
impossible I think, but thats logically waht it should mean.

Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

The range syntax is valuable mainly beacuse it creates a *standard* way of
expressing a large range of numbers compactly. Everyone can write their own
range class, but everyone's range class is probably going to have slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital to
the overreaching features I'm looking at. As I said early on, the original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins using
a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates a
list containing all three character long strings.

Now, I only hope these things help create a syntax that has a compelling
purpose, they may not. I certainly won't declare they are good ideas,
however its worth a shot isn't it?

Also, none of htese features are new. Its not a matter of "does it work",
but a matter of "does it work with C#"
 
M

Michael C

Daniel O'Connell said:
I am considering several things, like list comprehensions, which extend the
syntax sufficently that its valid outside of foreach and isin clauses. I'm
not sure I can do it, but I'm confident I can make a good run at it.

For starters, for dynamic ranges I hope to be able to optimize fairly well.
The syntax I am considering for basic dynamic ranges will be something like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

Is that what you mean?

No. This is still has a static declaration at run-time. The best example
of this goes back to joining multiple ranges together. For instance 1..100,
200..300 as in

int x = 1;
int y = 100;
foreach (a in x..y, 200..300)

Obviously you can change the values of x and y at run-time which gives a
little flexibility. If you have a need to dynamically add or remove ranges
to/from this foreach statement at run-time, how does the proposal address
this, other than creating another foreach loop? Here's an example:

bool userflag;
bool userflag2;
foreach (a in 1..100, 200..300) { /*code here*/ }
//now based on a flag set by a user selection I want to add another range
if (userflag)
foreach (a in 400..500) { /* programmer cuts and pastes same code from
loop above? */ }
//now based on a second flag we'll add another range
if (userflag2)
foreach (a in 600..700) { /* programmer cuts and pastes same code from
loop above? */ }

Now let's say we want to exclude the range 50..75 based on another user
flag, using the same code as above we replace the first foreach statement
with:

bool userflag3;
if (userflag3)
foreach (a in 1..49, 76..100, 200..300) { /* code here */ }
else
foreach (a in 1..100, 200..300) {/* cut and paste code from above */}

This is why I consider the solution to be non-dynamic, as each foreach loop
is broken down into un-changeable for statements at compile-time. Now if we
had 50 lines in each of the code blocks, we've suddenly expanded our program
to well over 250+ lines before we even bother expanding the foreach loops
into for loops, correct? As I said before, there is a trade-off, and the
performance will come at some cost. In this case the static nature of the
foreach (..) implementation gives us greater speed, but less flexibility in
defining and performing operations on ranges, and it expands the source code
to over 5X it's original size; which is not making the programmer's life
much easier IMHO.
As far as I'm concerned code like:
GetValue()...GetOtherValue();
should form a valid range expression. Its possible the range would be
strange(25...-2) for example, where the numbers will operate in the way
numbers do on computers strictly due to the way computers work. It has a
chance for a bug, but many features do.

I think code like that should form valid range expressions also, IMHO.
Also, IMHO, 1... means every number greater than one(this would stop as soon
as a number was less than one, in other words when wrapover occurs). and ....
would be effectivly an infinite sequence, or basically every number possible
for the assocaited type, starting from the minimum value of the type and
going to the max. Creating actual behaviour to that extent is pretty
impossible I think, but thats logically waht it should mean.

That makes sense. Creating a function (assuming there isn't one I've
overlooked) that returns the maximum and minimum values for any given
numeric type wouldn't be too difficult, one would think. Implementing the
.... operator would be the difficult part, I assume.
Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

I don't disagree with that. So are you simply defining a new syntax to make
declaring Lists easier? If so, why aren't you just iterating over a List in
the foreach statements?
The range syntax is valuable mainly beacuse it creates a *standard* way of
expressing a large range of numbers compactly. Everyone can write their own
range class, but everyone's range class is probably going to have slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital to
the overreaching features I'm looking at. As I said early on, the original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins using
a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates a
list containing all three character long strings.

99% of the time the programmer is not going to have a static list like
["mouse", "cat", "dog"] to put in their list as in the example above.
Obviously if I wanted to save myself typing 80% of what you did above, I
would simply type:

["cat", "dog"]

which is equivalent, no? The usefulness of this would come in when I can
change my list at run-time and perform that yield operation on a List
variable.
Now, I only hope these things help create a syntax that has a compelling
purpose, they may not. I certainly won't declare they are good ideas,
however its worth a shot isn't it?

Everything is worth a shot if there's someone willing to do it.
Also, none of htese features are new. Its not a matter of "does it work",
but a matter of "does it work with C#"

That makes sense, but again, the MER and the value proposition of the ranges
is small due to their limited scope and ability. As part of a simplified
List declaration syntax I can see your ranges adding value by reducing the
programmer's work significantly. But if this is what they reduce to, then
why use them outside of a List declaration scope at all? And why use them
independently of Lists at all? I.e., you don't foreach over a range, you
foreach over a List containing that range.

Michael C.
 
C

cody

Your solution is very limited in scope and functionality. It assumes that
everyone who decides to use ranges wants to be bound to a couple of strict
rules (overlapping members, static range declarations, ...), offering no
alternatives. All this is well and good, as the ranges are generated
non-dynamically at compile-time. This would only be a real issue if your
solution was flexible enough to produce ranges dynamically at run-time where
the following conditions apply:

-The programmer might not have absolute control over which items are
included in the range to be looped over
-The programmer wants a specific behavior every time regardless of the items
to be included

Be honest: In 99% of all cases you use simple loops like for (int i=0;
i<100; i++).
Often they loop reverse or they are nested. And our proposal is just for
these cases.

I can hardly imagine a scenario where you really want to enumerate over
multiple ranges and the ranges are know during runtime only and additionally
you want to exclude numbers from your enumeration.
This is somewhat rarely for such a case I'd rather use an Enumerator which
can be made so flexible that is covers all cases.

Don't you agree that it would be a bit stupid to invent a language feature
for a case which is so rarely used?
 
C

cody

(See my post where I mention the usefulness of the "?:" operator). I agree
totally, and if static range declarations that are locked in place at
compile time is the answer to your problems, more power to ya. But
considering the static nature of these range declarations, what is the
usefulness of combined range declaration like 0..100, 50..150 in a foreach
loop? It seems like the MER (Maximum Effective Range) of the following
statement would be nil:

foreach (a in 0..100, 50..150)


This loop does 202 iterations. I hope you've read the thread.
 
C

cody

I am concerned with the # syntax due to # being used already by compiler
directives.

Damn you're right. I fear that there is no single-char operator left to use.
Maybe <- will be the right choice :)
 
C

cody

For starters, for dynamic ranges I hope to be able to optimize fairly
well.
The syntax I am considering for basic dynamic ranges will be something like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}


I found a problem. What if x is greater than y? In that case the loop has to
loop backward and this
decision can only be determined at runtime, so the generated for loop will
look a bit more complicated:

float inc;
float sgn;
if (x>y)
{
inc = - 1f;
sgn = - 1f;
y= - (y+1);
}
else
{
inc = 1f;
sgn = 1f;
y++;
}

for (int i = x; sgn * i <= y; i+=inc)
{
//code
}
 
D

Daniel Billingsley

cody said:
I found a problem. What if x is greater than y? In that case the loop has to
loop backward and this
decision can only be determined at runtime, so the generated for loop will
look a bit more complicated:

That seems like a big assumption! Often x being greater than y indicates
the loop should not be performed at all.
 
D

Daniel O'Connell [C# MVP]

Daniel Billingsley said:
That seems like a big assumption! Often x being greater than y indicates
the loop should not be performed at all.
Or even that you should use wrap-around semantics. Determining that
behaviour could be rather difficult but not impossible.

This is a semantic area that needs definition, however. Do ranges always
head fowards or do they take go towards y?
 
M

Michael C

cody said:
This loop does 202 iterations. I hope you've read the thread.

So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness
of the foreach example above?

Michael C.
 
M

Michael C

Be honest: In 99% of all cases you use simple loops like for (int i=0;
i<100; i++).
Often they loop reverse or they are nested. And our proposal is just for
these cases.

Be honest. Based on yours and Daniel's posts you have proposed lot more
than a short-hand 'for' loop. If it's a simplified syntax for a for loop
you're looking for, then it's not nearly as complex as you make it. You've
been suggesting multiple linked ranges, ranges seen as subsets of Lists (yet
not part of a class), etc. If you're covering only the 99% of for loops out
there, then be honest - why do you need all this other stuff? This
statement:

foreach (i in 1..100, 50..150)

Falls WAY outside your 99% rule. I'd be surprised to find anyone, outside
of some extreme statistical analysis maybe, who would need a loop like this
for anything. I could be wrong though - maybe you can enlighten me on the
reasoning and usage of a looping structure like this?
I can hardly imagine a scenario where you really want to enumerate over
multiple ranges and the ranges are know during runtime only and additionally
you want to exclude numbers from your enumeration.
This is somewhat rarely for such a case I'd rather use an Enumerator which
can be made so flexible that is covers all cases.

I can imagine many more times where you really want to enumerate over
multiple ranges and the ranges are known only at run-time than scenarios in
which you want to enumerate over multiple ranges and the ranges are known in
advance. I've given you examples; the prime one being a range which is
iterated over in which the user specifically chooses to exclude certain
sub-ranges or elements. I'd love to hear some examples of enumerating over
multiple ranges and knowing all the ranges at design-time.
Don't you agree that it would be a bit stupid to invent a language feature
for a case which is so rarely used?

Exactly! Now you're getting it!
 
D

Daniel O'Connell [C# MVP]

Michael C said:
No. This is still has a static declaration at run-time. The best example
of this goes back to joining multiple ranges together. For instance
1..100,
200..300 as in

int x = 1;
int y = 100;
foreach (a in x..y, 200..300)

Obviously you can change the values of x and y at run-time which gives a
little flexibility. If you have a need to dynamically add or remove
ranges
to/from this foreach statement at run-time, how does the proposal address
this, other than creating another foreach loop? Here's an example:

bool userflag;
bool userflag2;
foreach (a in 1..100, 200..300) { /*code here*/ }
//now based on a flag set by a user selection I want to add another range
if (userflag)
foreach (a in 400..500) { /* programmer cuts and pastes same code from
loop above? */ }
//now based on a second flag we'll add another range
if (userflag2)
foreach (a in 600..700) { /* programmer cuts and pastes same code from
loop above? */ }

Now let's say we want to exclude the range 50..75 based on another user
flag, using the same code as above we replace the first foreach statement
with:

bool userflag3;
if (userflag3)
foreach (a in 1..49, 76..100, 200..300) { /* code here */ }
else
foreach (a in 1..100, 200..300) {/* cut and paste code from above */}

Well, like many things, you use a slightly different pattern here.

IEnumerable<int> enum= 1...100,1...200;
IList<int> list;
if (userflag)
enum = enum,400...500;
if (userflag2)
enum = enum,500...600;
if (userflag3)
list = [yield value for enum where !(value isin [50...75])];
else
list = [enum];

foreach (int i in list)
{
//code
}
(note the syntax isn't finalized yet. This will result in different
enumerators for each value. However I'm not terribly thrilled with the
multiple enumerator definition syntax)

is probably what I would do. This is precisely where list comprehensions
come in, they allow you to modify what ranges and other lists generate for
your list. This is a circumstance that I consider to be too complex to
achieve for optimization, this would result in a foreach loop going over a
list containing the values(although I would probably design the list so that
it doesn't generate values until used, taking up less space in the case of
large numbers).
This is why I consider the solution to be non-dynamic, as each foreach
loop
is broken down into un-changeable for statements at compile-time. Now if
we
had 50 lines in each of the code blocks, we've suddenly expanded our
program
to well over 250+ lines before we even bother expanding the foreach loops
into for loops, correct? As I said before, there is a trade-off, and the
performance will come at some cost. In this case the static nature of the
foreach (..) implementation gives us greater speed, but less flexibility
in
defining and performing operations on ranges, and it expands the source
code
to over 5X it's original size; which is not making the programmer's life
much easier IMHO.

Breaking down to for loops only happens when the pattern used allows it.
Using literal patterns in a foreach as such is identical to using them in a
for. There is no way to dynamically change it anymore than its possible to
change an upper bound in a for. However, in both situations you can use
other patterns, as I showed above, to achieve what you want with a minimum
of work.
I think code like that should form valid range expressions also, IMHO.

As cody pointed out, there is a difficulty in getting values to behave
correctly here, however. I am concerned that dynamic sources could be
confusing. But that will be addressed when I come to it.
That makes sense. Creating a function (assuming there isn't one I've
overlooked) that returns the maximum and minimum values for any given
numeric type wouldn't be too difficult, one would think. Implementing the
... operator would be the difficult part, I assume.

It won't be difficult for the built in types, its more custom valuetypes I'm
concerned with. Since the design supports arbitrary increments its possible
some more complex logic needs to be defined to deal with that cirucmstance.

Basically, IMHO, max is the value where adding one to it would make it less
than itself.
Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

I don't disagree with that. So are you simply defining a new syntax to
make
declaring Lists easier? If so, why aren't you just iterating over a List
in
the foreach statements?

Defining lists is a core purpose, as is ranges.
The range syntax is valuable mainly beacuse it creates a *standard* way
of
expressing a large range of numbers compactly. Everyone can write their own
range class, but everyone's range class is probably going to have
slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital to
the overreaching features I'm looking at. As I said early on, the
original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins using
a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates a
list containing all three character long strings.

99% of the time the programmer is not going to have a static list like
["mouse", "cat", "dog"] to put in their list as in the example above.
Obviously if I wanted to save myself typing 80% of what you did above, I
would simply type:

["cat", "dog"]

which is equivalent, no? The usefulness of this would come in when I can
change my list at run-time and perform that yield operation on a List
variable.

Yes it is. However you can use *any* IEnumerable object in your list
comperehension and that object will be iterated and yielded into the new
list. I was trying to figure out a way to handle multiple source lists in a
comprehension cleanly, but I have failed so far. To do it cleanly requires
implicit typing and other, less appealing, syntactic changes.
Everything is worth a shot if there's someone willing to do it.


That makes sense, but again, the MER and the value proposition of the
ranges
is small due to their limited scope and ability. As part of a simplified
List declaration syntax I can see your ranges adding value by reducing the
programmer's work significantly. But if this is what they reduce to, then
why use them outside of a List declaration scope at all? And why use them
independently of Lists at all? I.e., you don't foreach over a range, you
foreach over a List containing that range.

Well, optimization oppurtunity is one reason. The other and more pressing
reason is that I dislike creating an expression that is only valid within
another expression even if the expression could be used elsewhere. As I
pointed out above to create your dynamic enumerable, you can use those
directly without generating a list. If there is any reaon use for them
outside of list's, I can't say. However from a purists point of view I think
a user should be allowed to use them as such if he or she wishes.

As far as the compiler is concerned 1...100 is simply an IEnumerable<int>
expression and it operates upon it as it is. It may do extra checks to see
if the expression is a range that can be optimized, but beyond that it
doesn't treat it as a range, it treats it as an IEnumerable<int>.

This isnt to say someone could argue me down into putting ranges into lists
only, its just my current point of view.
 
C

cody

int x = 50;
Or even that you should use wrap-around semantics. Determining that
behaviour could be rather difficult but not impossible.

This is a semantic area that needs definition, however. Do ranges always
head fowards or do they take go towards y?


I would say that ranges should always loop from the first given value to the
second, that's why they are called ranges.

foreach (int a in 0..100) // forward
foreach (int a in 100..0) // backward

If somebody only want to loop only forward or only backward (that is, the
direction is know at compile time) he can use a normal loop.
 
C

cody

(See my post where I mention the usefulness of the "?:" operator). I
So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness
of the foreach example above?


My original proposal was a simple loop using a *single* range. multiple
ranges and other possible extensions are still discussed and nobody claimed
here that they *are* really useful, just that they *could* be useful.
They *can* be very useful in situations like if (myChar in
['a'..'z','A'..'Z']). Don't tell me about regex,
first regex would be slower in this situation and second this was just an
example.
 
M

Michael C

message
Thank you for your post. Your explanations help quite a bit. Of course
there's always the trade-off between optimization and functionality. It
would be nice to have the ability to perform operations like + to join two
lists together, or - to remove elements, or even * to join two lists without
overlap. This would make the previous examples a lot simpler, as you could
just use statements like:

myList = [1..1000];
if (userflag)
myList -= [400..500];
if (userflag2)
myList -= [600..700];
if (userflag3)
myList -= [50..75];
foreach (i in myList)

Thanks,
Michael C.
 
M

Michael C

cody said:
So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness
of the foreach example above?


My original proposal was a simple loop using a *single* range. multiple
ranges and other possible extensions are still discussed and nobody claimed
here that they *are* really useful, just that they *could* be useful.
They *can* be very useful in situations like if (myChar in
['a'..'z','A'..'Z']). Don't tell me about regex,
first regex would be slower in this situation and second this was just an
example.

Now, going back to your original proposal. A simple loop with a single
range - is that your current proposal? You've used the overlapping ranges
in numerous examples yourself, and expressly proposed that the behavior is
different than a 'set', since duplicates would be allowed in overlapping
ranges. (myChar in ['a'..'z', 'A'..'Z']) is definitely a LOT more useful
than (myChar in ['a'..'z', 'h'..'w', 'A'..'Z', 'L'..'O']), but again you're
*not* overlapping in your example above. IMHO I don't see any place that
overlapping ranges, when used in your paradigm *could* be useful. Much less
*are* useful. I could see application for non-overlapping ranges; but if
your proposal is a simple short-hand single range for foreach loops, then
99% of this thread is moot.

Enjoy,
Michael C.
 
M

Michael C

cody said:
I would say that ranges should always loop from the first given value to the
second, that's why they are called ranges.

foreach (int a in 0..100) // forward
foreach (int a in 100..0) // backward

If somebody only want to loop only forward or only backward (that is, the
direction is know at compile time) he can use a normal loop.

Actually he's talking about optimization in relation to the issue I brought
up earlier about creating *dynamic* ranges. I think you answered that one
with a 'no problem'. Well there is a slight problem:

int x = 100;
int y = 200;
foreach (a in x..y)

According to your theory, this should be expanded to

for (a = x; a <= y; a++)

Well, if the value of x becomes greater than y, and you want the behavior
described above, you can't very well optimize it at compile time, can you?

int x = 100;
int y = 200;
if (myFlag)
{
x = 200;
y = 100;
}
foreach (a in x..y)

Does this statement optimize with:

for (a = x; a <= y; a++)

Or is it:

for (a = x; a >= y; a--)

Guess we'll find out at run-time, right?

Thanks,
Michael C.
 
D

Daniel Billingsley

Michael C said:
the

Actually he's talking about optimization in relation to the issue I brought
up earlier about creating *dynamic* ranges. I think you answered that one
with a 'no problem'. Well there is a slight problem:

I think it goes even beyone that if you're talking about dynamic ranges -
which I was. It may be the case in a particular situation that x is
sometimes going to be less than y and sometimes greater than y, and the
desired behavior is actually to not run the loop if it is greater, just like
in a normal for loop. So now according to cody's proposal in that case
you'd have to wrap this new style for loop in an if statement, and perhaps
more importantly, remember that you have to. yuck.
 
D

Daniel O'Connell [C# MVP]

Michael C said:
message
Thank you for your post. Your explanations help quite a bit. Of course
there's always the trade-off between optimization and functionality. It
would be nice to have the ability to perform operations like + to join two
lists together, or - to remove elements, or even * to join two lists
without
overlap. This would make the previous examples a lot simpler, as you
could
just use statements like:

myList = [1..1000];
if (userflag)
myList -= [400..500];
if (userflag2)
myList -= [600..700];
if (userflag3)
myList -= [50..75];
foreach (i in myList)

I have two issues here. One is that I don't like adding psuedo-operators to
types which *may* contain real operators at some point. IList and List don't
provide any, but that doesn't mean other types that implement them won't.
I'm more comfortable using comprehensions to perform these operations,
although I've yet to figure out a way to write list unions and intersections
easily(My multi-source syntax worked, but I really disliked the implicit
typing that resulted).

The other issue, -[50...75] is confusing. Is it going to remove *every*
instance of 50...75 or just the first one, or just a segment running from 50
to 75? With a comprehension exactly what is going to happen is spelled out,
there is no room for confusion.

At this point I'm starting to really fret. I don't know how to achieve a
complete and cohesive solution. Every time I get a set of rules working I
find a fundamental type that violates those rules. I'm having a tough time
with strings now, a string is enumerable but will rarely be wanted to be
considered as such. I am probably going to have to define an override
operator(something that says "put this list in the list" instead of "put
this list's contents in the list", any recommendations on syntax?
 
M

Michael C

I have two issues here. One is that I don't like adding psuedo-operators to
types which *may* contain real operators at some point. IList and List don't
provide any, but that doesn't mean other types that implement them won't.
I'm more comfortable using comprehensions to perform these operations,
although I've yet to figure out a way to write list unions and intersections
easily(My multi-source syntax worked, but I really disliked the implicit
typing that resulted).

If other types implement these operators at some point, wouldn't it be
incumbent on the programmer to overload the operators if they wanted
different behavior?
The other issue, -[50...75] is confusing. Is it going to remove *every*
instance of 50...75 or just the first one, or just a segment running from 50
to 75? With a comprehension exactly what is going to happen is spelled out,
there is no room for confusion.

Confusion is at the core of this project. Not a lot of the desired end
results have been agreed upon, nor the underlying problem defined. One
person defines the problem as simple x..y ranges, in which overlapping
segments make no sense; one sees the usefulness of multiple ranges with
endpoints that are non-contiguous, another thinks overlap may be useful
somewhere down the road to somebody; another person sees ranges as being
part of a larger list type which may contain overlapping ranges or
non-contiguous ranges. So let's take a step back.

If we are looking at [1..100] as a list, which may contain multiple ranges,
then let's start with the + operator. If the list allows multiple ranges,
the + operator would produce the following result:

[1..100] + [25..75] = [1..100, 25..75]

Therefore the += operator would produce similar results:

x = [1..100];
x += [25..75]; // x = [1..100, 25..75]

I suggested the * operator because a programmer might want to produce a
single range with no overlap in some situations (i.e., looping where overlap
makes no sense). The | operator might make sense here also:

[1..100] * [25..150] = [1..150]

This leads to the shorthand *= also. As for the minus operator, that is
dependent partially on the definition of the actual problem from above.
Regardless, my thoughts are that most programmers expect their minus
operator to work inversely to the plus operator. So the - operator, as
inverse of the + operator, would provide the following:

x = [1..100, 25..75];
x += [25..75]; // x = [1..100, 25..75, 25..75]
x -= [25..75]; // x = [1..100, 25..75] - removes one range, as + adds one
range

Perhaps an operator could be agreed upon that also removes all of a certain
type of ranges (i just put in the ! cause I'm running out of keyboard
chars -- think of it as a placeholder):

x = [1..100, 25..75];
x != [25..75]; // removes all subranges 25..75 from the list

These are just ideas, but the problem has to be clearly defined first.
At this point I'm starting to really fret. I don't know how to achieve a
complete and cohesive solution. Every time I get a set of rules working I
find a fundamental type that violates those rules. I'm having a tough time
with strings now, a string is enumerable but will rarely be wanted to be
considered as such. I am probably going to have to define an override
operator(something that says "put this list in the list" instead of "put
this list's contents in the list", any recommendations on syntax?

Don't fret, just take a step back and clearly define the problem in terms of
the end result. I have a pretty good idea of the end result I would like,
but it doesn't necessarily mesh with all the other ideas being thrown
around. With that in mind, how about + to put a list in the list, and | to
put the list's contents in the list w/ overlap and * to copy contents w/o
overlap... In that case:

[1..100] + [25..75] = [1..100, [25..75]]
[1..100] | [25..75] = [1..100, 25..75] // if this operator were implemented
with overlap
[1..100] * [25..150] = [1..150] // if this operator were implemented with no
overlap

How you would implement the list inside of a list, and what operator - if
any - to remove items from the list, is another story...

Thanks,
Michael C.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top