foreach enhancement

J

Jon Skeet [C# MVP]

Czhen du Loc Pha said:
This person seems to understand.

Well, I'd say he seems to understand that generics will provide an
easier way of creating type-safe sets. That's not the same as providing
extra syntax in the language to make them *really* easy, as cody was
requesting.
http://weblogs.asp.net/fmarguerie/archive/2003/11/10/36710.aspx

He talks about enums and sets, then goes on to mention how c# doesn't have
generics yet, which would be the more compact way to do what the OP wants.

He doesn't claim that generics are going to change the language in the
way suggested by cody, however.

Sure, type-safe sets themselves will be easier to write when we've got
generics - but they aren't going to make it possible to write

foreach (int i in [1..50, 50..100])

The original post I replied to in this subthread seemed to imply that
generics would provide the answer to cody's original request. They
won't.
 
C

cody

So going back to the 'set' analogy, [1..50, 50..100] isn't a union of
sets,
but rather shorthand for two separate sets?


It is a list of ranges to be precise. The term "set" seems indeed not
correct for that construct.
The ranges might overlap, and a foreach iterates over *all* values in the
set/list/whatever in the order they appear in the declaration. if used in if
(a in [1..50, 50..100]) the compiler should optimize away duplicates.
 
C

cody

Why not just making {1,2,3} an array literal? It is just a shorthand
for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically
possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell
the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes
with
a
single contained semicolon.


We could simply say if a {} expression contains at least one semicolon or
is
emtpy
then it is a codeblock, otherwise it is an array literal. An array
literal
cannot be empty
(the compiler would not be able to determin the type) and is not allowed
to
contain semicolons.

That shows that you have never written a compiler. Its not easy to
determine if a semicolon is contained within any given block as its
handled by a parser.
{ } // block
{ Foo(); } // block
{1} // array literal
{1,2} // array literal

what about
{
delegate(int x)
{
return 50;
}
}
?

Thinking about it a bit more, it may be possible using something
like(hopefullyu I got the syntax right, don't have a file open on hand)
array_literal_expression : OPEN_BRACKET expression_list CLOSE_BRACKET
;
expression_list
: expression
| expression_list expression
;

its worth looking into, I'll probably delve into mono again in a day or two
and see what can be done.


That would be very great :)
 
C

cody

Why not just making {1,2,3} an array literal? It is just a shorthand
for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically
possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell
the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes
with
a
single contained semicolon.


We could simply say if a {} expression contains at least one semicolon or
is
emtpy
then it is a codeblock, otherwise it is an array literal. An array
literal
cannot be empty
(the compiler would not be able to determin the type) and is not allowed
to
contain semicolons.

That shows that you have never written a compiler. Its not easy to
determine if a semicolon is contained within any given block as its
handled by a parser.
{ } // block
{ Foo(); } // block
{1} // array literal
{1,2} // array literal

what about
{
delegate(int x)
{
return 50;
}
}
?

Thinking about it a bit more, it may be possible using something
like(hopefullyu I got the syntax right, don't have a file open on hand)
array_literal_expression : OPEN_BRACKET expression_list CLOSE_BRACKET
;
expression_list
: expression
| expression_list expression
;

its worth looking into, I'll probably delve into mono again in a day or two
and see what can be done.


With brackets[] you mean braces{} don't you?
 
D

Daniel O'Connell [C# MVP]

Thinking about it a bit more, it may be possible using something
like(hopefullyu I got the syntax right, don't have a file open on hand)
array_literal_expression : OPEN_BRACKET expression_list CLOSE_BRACKET
;
expression_list
: expression
| expression_list expression
;

its worth looking into, I'll probably delve into mono again in a day or two
and see what can be done.


With brackets[] you mean braces{} don't you?
Yes, I think that the mono parser calls { OPEN_BRACKET, but I could be
wrong. I forget what [ is identified as.
Some of the names in that parser are a little strange.

Heres my plan for the moment, let me know what you think:

1) m...n is a range generator. It returns am IEnumerable. It can be assigned
to any IEnumerable variable or used as IEnumerable in foreach or as a method
parameter.
2) [<enumerableExpression>,<literalExpression>,<sourceExpression>] generates
an IList containing those values.
2b) Lists take the best possible type. If your enumerable expression returns
int and you have a long literal, the list is IList<long>, if it returns ints
and you have a double literal, IList<double>, etc. If the list contains
heterogeneous types, such as int and string or int and date time, it is
typed as IList<object>. I still have to consider the exact rules of such.
3) {<enumerableExpression>,<literalExpression>,<sourceExpression>] generates
an array containing those values.
3b) For arrays all values have to be of the same type. Array literals take
on the numeric behaviour where the largest nessecery type is used, but
arrays cannot contain multiple types.
4) For enumerableExpression, every value is read out *unless* the expression
is cast to object or somesuch. This does leave a problem in that there is no
way to generate a list of IEnumerables, but if someone wants that edge case
they should probably use long-hand list or array syntax. Can you think of
any workarounds for this?
enumerableExpression is typed either object for IEnumerable or T for
IEnumerable<T>.
5) sourceExpression is any expression that returns a type, be it
mathematical or a method call. These aren't literals in a very strict sense,
every one of these values is generated at run time(unless compiletime
generation is possible).
6) Where possible, foreach over a list or generator will generate a for
loop. This will only occur if the list generates integers or other
structures that support > <, etc. If the compiler cannot determine a valid
for loop or if code complexity would become to great(too many ranges or
multiple types), the compiler will instead generate the list and foreach
will operate over that lists IEnumerable implementation.
7) Valid range increment values are *any* value the range type has a
operator+ defined for. The operator+ must return the range type however.
8) For contains checking, I'm still much more comfortable with isin over in,
so I will likely implement that keyword as it stands. However I will
consider adding a pragma that changes support to in so that eitehr can be
experimented with.

9) Experimental: If code contains the same range type twice only one
IEnumerable\IEnumerator class pair will be generated. That pair will take
parameters that determine ranges. This should cut down on class bloat

Well, thats a basic outline for what I'll do(assuming I can find time). Any
comments from anyone?
--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
M

Michael C

Jon Skeet said:
So going back to the 'set' analogy, [1..50, 50..100] isn't a union of sets,
but rather shorthand for two separate sets?

When used in a foreach statement, it shouldn't be regarded as a set at
all - it should be regarded as a sequence, IMO.

OK, this is purely hypothetical, so bear with me and be kind if I'm Wayyyyy
off base:

The overloaded brackets are confusing. Would it be possible to create a new
Sequence class that extends the Collection class, overloads the "+" operator
to act as a join on the sequences, and takes advantage of the IEnumerator
interface for foreach operations? Example:

foreach (int a in Sequence(1, 50) + Sequence(25, 100)) // + operator
overloaded to "join" the 2

// sequences, with overlap so would return

// 1..50, 25..100

Maybe we could use params in the constructor to implement a constructor that
allows us to create multiple Sequences at once?

foreach (int a in Sequence(1, 50, 25, 100)) // creates two sequences, 1..50
and 25..100 and
// joins
them into one for us with overlap; shorthand for +

This has the additional advantage of letting us create dynamic sequences
using variables (not just constants) on the fly:

int x = 1;
int y = 50;
foreach (int a in new Sequence (x, y))

Finally, if we feel it's necessary we can overload the other operators.
Here's a suggestion:

Sequence(1, 50) - Sequence(3, 8) // subtracts or removes a sequence. would
give us 1..2, 9..50
Sequence(1, 50) * Sequence (25, 75) // as opposed to +, this would give us a
union with no overlaps. this
// would be a
'combine' operation as opposed to an
// overlapping
join so would return 1..75

Just a thought.

Thanks,
Michael C.
 
C

cody

OK, this is purely hypothetical, so bear with me and be kind if I'm
Wayyyyy
off base:

The overloaded brackets are confusing. Would it be possible to create a new
Sequence class that extends the Collection class, overloads the "+" operator
to act as a join on the sequences, and takes advantage of the IEnumerator
interface for foreach operations? Example:

foreach (int a in Sequence(1, 50) + Sequence(25, 100)) // + operator
overloaded to "join" the 2

// sequences, with overlap so would return

// 1..50, 25..100

Maybe we could use params in the constructor to implement a constructor that
allows us to create multiple Sequences at once?

foreach (int a in Sequence(1, 50, 25, 100)) // creates two sequences, 1..50
and 25..100 and
// joins
them into one for us with overlap; shorthand for +

This has the additional advantage of letting us create dynamic sequences
using variables (not just constants) on the fly:

int x = 1;
int y = 50;
foreach (int a in new Sequence (x, y))

Finally, if we feel it's necessary we can overload the other operators.
Here's a suggestion:

Sequence(1, 50) - Sequence(3, 8) // subtracts or removes a sequence. would
give us 1..2, 9..50
Sequence(1, 50) * Sequence (25, 75) // as opposed to +, this would give us a
union with no overlaps. this
// would be a
'combine' operation as opposed to an
// overlapping
join so would return 1..75



This would all certainly be possible, without any problem. Such a sequence
class can be written in half an hour, but that is not the point here. The
idea was to invent a new language feature which allow the programmer to loop
over multiple ranges and test wheather a value exists in a range with a
simple built in syntax and *without* creating any temporary objects.
 
C

cody

Heres my plan for the moment, let me know what you think:
1) m...n is a range generator. It returns am IEnumerable. It can be assigned
to any IEnumerable variable or used as IEnumerable in foreach or as a method
parameter.
2) [<enumerableExpression>,<literalExpression>,<sourceExpression>] generates
an IList containing those values.
2b) Lists take the best possible type. If your enumerable expression returns
int and you have a long literal, the list is IList<long>, if it returns ints
and you have a double literal, IList<double>, etc. If the list contains
heterogeneous types, such as int and string or int and date time, it is
typed as IList<object>. I still have to consider the exact rules of such.
3) {<enumerableExpression>,<literalExpression>,<sourceExpression>] generates
an array containing those values.
3b) For arrays all values have to be of the same type. Array literals take
on the numeric behaviour where the largest nessecery type is used, but
arrays cannot contain multiple types.

We also could say that for objects we make the array of a type that is
common base class of *all* of the array members and supports all interfaces
which *all* objects in the array support:

{string, object} // array type is object
{IList, Array} // array type if object supporting IList
{Control, TextBox} // array type is Control

If the array contains value types *and* reference types, the type of the
array is object.
If only numeric types are involved I agree that the array should be of the
type of the biggest nessecary numeric type.
4) For enumerableExpression, every value is read out *unless* the expression
is cast to object or somesuch. This does leave a problem in that there is no
way to generate a list of IEnumerables, but if someone wants that edge case
they should probably use long-hand list or array syntax. Can you think of
any workarounds for this?
enumerableExpression is typed either object for IEnumerable or T for
IEnumerable<T>.

I do not like that Idea. It would mean that you cannot put any object
supporting IEnumerable into the array without splitting it up into its
items. Better would be IEnumerator instead of IEnumerable, which means
you'll have to put list.GetEnumerator() into the array so it'll be splitted
up. But I still do not like that idea. Array have a fixed size and allowing
IEnumerators to be inserted into array literals means that during runtime
you have to create temporary lists and copy from them into the destination
array since you cannot resize it.
e.g:

{obj1, obj2, list1}

Assumed you want to guarantee that the expressions are evaluated in the
order they appear in the declaration you have first to create a temporary
list. you put obj1 and obj2 in it. then you have to evaluate list1 and add
its contents to the list. after you've evaluated all objects you can call
..ToArray() and your array is constructed.
But again, I do not like the idea of expanding IEnumerables. If I declare an
array literal containg 3 elements I expect that is really has 3 elements and
not more.
5) sourceExpression is any expression that returns a type, be it
mathematical or a method call. These aren't literals in a very strict sense,
every one of these values is generated at run time(unless compiletime
generation is possible).
agreed.

6) Where possible, foreach over a list or generator will generate a for
loop. This will only occur if the list generates integers or other
structures that support > <, etc. If the compiler cannot determine a valid
for loop or if code complexity would become to great(too many ranges or
multiple types), the compiler will instead generate the list and foreach
will operate over that lists IEnumerable implementation.

You are talking about set literals now? We should clearly separate the short
array literal syntax and the multi-range-set-literal syntax, they have
nothing to do with each other.
7) Valid range increment values are *any* value the range type has a
operator+ defined for. The operator+ must return the range type however.
agreed.

8) For contains checking, I'm still much more comfortable with isin over in,
so I will likely implement that keyword as it stands. However I will
consider adding a pragma that changes support to in so that eitehr can be
experimented with.

I do not agree but you are the one who implements it :)
9) Experimental: If code contains the same range type twice only one
IEnumerable\IEnumerator class pair will be generated. That pair will take
parameters that determine ranges. This should cut down on class bloat

Good idea. The bad thing is that generics do not support operator
overloading which means we cannot use generics here and we must create a new
class for each type.

However, I still think that it is still possible to implement this
sets/ranges without the creation of any classes/objects. It will maybe make
the implemention harder but will make the code faster and do not need
temporary objects and a bloat of classes.

"if (a in [0..100, 200..300])" should only be a substitute of
"if ((a>=0&&a<=100)||(a>=200&&a<=300))",

and "foreach (a in 0..100, 200,300)" should simply be compiled to
"for (int a=0;a<=100;a++){} for (int a=200;a<=300;a++){}"

But this is only implementation detail, no programmer using the language
should be affected on how it is implemented.
A simply implementation as a proof of concept should be enough for now and
hopefully our ideas will make it into regular c# someday :)
 
M

Michael C

This would all certainly be possible, without any problem. Such a sequence
class can be written in half an hour, but that is not the point here. The
idea was to invent a new language feature which allow the programmer to loop
over multiple ranges and test wheather a value exists in a range with a
simple built in syntax and *without* creating any temporary objects.

Ahh, I thought the point was to be able to create sequences that would allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.

I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order to
accomplish something that can be done in half an hour.

Best of luck with that.

Thanks,
Michael C.
 
C

cody

This would all certainly be possible, without any problem. Such a
sequence
Ahh, I thought the point was to be able to create sequences that would allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.

Indeed, that was the point.
I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order to
accomplish something that can be done in half an hour.

You'll agree, that foreach (a in 1..100, 200..300) is far better than

foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))

don't you? And exactly that is the purpose of my proposal.
Loops are one of the most common used things and making them more readable
and maintainable should be a desirable goal, don't you agree?

Using the syntax I proposed, the compiler will be able to substitute the
foreach loop to a normal
for loop so you won't have any performance penalty and there is no need for
creating additional classes and temporary objects.

I encourage you to read the whole thread to understand how these constructs
can improve code quality (we are not only talking about foreach loops here).
 
D

Daniel O'Connell [C# MVP]

We also could say that for objects we make the array of a type that is
common base class of *all* of the array members and supports all
interfaces
which *all* objects in the array support:

{string, object} // array type is object
{IList, Array} // array type if object supporting IList
{Control, TextBox} // array type is Control

sounds right.
If the array contains value types *and* reference types, the type of the
array is object.
If only numeric types are involved I agree that the array should be of the
type of the biggest nessecary numeric type.


I do not like that Idea. It would mean that you cannot put any object
supporting IEnumerable into the array without splitting it up into its
items. Better would be IEnumerator instead of IEnumerable, which means
you'll have to put list.GetEnumerator() into the array so it'll be
splitted
up. But I still do not like that idea. Array have a fixed size and
allowing
IEnumerators to be inserted into array literals means that during runtime
you have to create temporary lists and copy from them into the destination
array since you cannot resize it.
e.g:

{obj1, obj2, list1}

Assumed you want to guarantee that the expressions are evaluated in the
order they appear in the declaration you have first to create a temporary
list. you put obj1 and obj2 in it. then you have to evaluate list1 and add
its contents to the list. after you've evaluated all objects you can call
.ToArray() and your array is constructed.
But again, I do not like the idea of expanding IEnumerables. If I declare
an
array literal containg 3 elements I expect that is really has 3 elements
and
not more.
The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without breaking
the rules of the language is pretty tough. I've been considering a new
operator, <-, which assigns each value in an enumerable object to a variable
You are talking about set literals now? We should clearly separate the
short
array literal syntax and the multi-range-set-literal syntax, they have
nothing to do with each other.

I'm talking optimization of range literals. For the case foreach (int i in
1...1000) I see no reason to generate an object, instead a for loop like you
suggested is generatable. However, for code like
foreach(int i in 1...1000, 1000...1, 2...5 : .5, 3...5838) a generator
object would probably make more sense as it would *vastly* simplify the
output code and probably the break statement as well.
I do not agree but you are the one who implements it :)

Yeah, there is a little bit of a boon to that sometimes, ;).
9) Experimental: If code contains the same range type twice only one
IEnumerable\IEnumerator class pair will be generated. That pair will take
parameters that determine ranges. This should cut down on class bloat

Good idea. The bad thing is that generics do not support operator
overloading which means we cannot use generics here and we must create a
new
class for each type.

However, I still think that it is still possible to implement this
sets/ranges without the creation of any classes/objects. It will maybe
make
the implemention harder but will make the code faster and do not need
temporary objects and a bloat of classes.

"if (a in [0..100, 200..300])" should only be a substitute of
"if ((a>=0&&a<=100)||(a>=200&&a<=300))",

and "foreach (a in 0..100, 200,300)" should simply be compiled to
"for (int a=0;a<=100;a++){} for (int a=200;a<=300;a++){}"

But this is only implementation detail, no programmer using the language
should be affected on how it is implemented.
A simply implementation as a proof of concept should be enough for now and
hopefully our ideas will make it into regular c# someday :)

In many situations, the shortcuts will work(and I will use them wehre
possible). However, as a underlying foundation each form of syntax does
return a value and exists as an expression unto itself:
1...1000 is "A sequence that runs from the number one to the number 1000,
inclusive" and is encapsulated in an IEnumerable(I'm not using IEnumerator
due to IEnumerator<T> not supporting Reset. to have a reusable set you need
to be able to reset the resultant enumerator, thus GetEnumerator must be
accessible).
[1...1000] is "A list generated from a enumerable sequence"
{1...1000} is "An array generated from an enumerable sequence".

As such, each can stand alone. The stand alone objects can then be used in
other situations, vastly expanding (potential) usage.

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

But, as I said these are *very* rough and I'm not sure how or if I'll
bother. I do like the concept though and they are quite popular in python.

I'll post more on syntax reasoning if anyone is interested.
--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
D

Daniel O'Connell [C# MVP]

, every value is read out *unless* the
The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without
breaking the rules of the language is pretty tough. I've been considering
a new operator, <-, which assigns each value in an enumerable object to a
variable
Err, I didn't finish that.

I've been considering it but Ican't find a clean way to add it. Adding
enumerable objects contents to lists and arrays is vital, and it is vital to
do with with IEnumerable. Remember that there is *no* magic in a compiler,
atleast not in a C based language compiler. Everything has to work out in
the end.

How do you express one without the other?
 
D

Daniel O'Connell [C# MVP]

Michael C said:
Ahh, I thought the point was to be able to create sequences that would
allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.

As cody said, the original goal was to make for's easier to write, in
esscense. Its not quite the same because you can't manually advance i, but
still, that was the goal.
The goal grew, for me anyway, to permit the language to succinctly express
lists and some list operations without using explict classes. Much of what I
want to see isn't expressly possible with existing syntax and having to
write a method to do it for each situation is overkill(one method is 10
minutes maybe, but if you have to do it a hundred times a month?). A good
deal of what I'm looking at and driving towards is python\haskell like lists
and list comprehensions.
I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order
to
accomplish something that can be done in half an hour.
Language syntax extension isn't the only acceptable solution(although it is
for the original concept). However I don't think there is any harm in
exploring new syntax that would ease certain concepts in the language. After
all, there are quite a few things you don't *have* to have in the language
that are quite useful, like using, foreach, lock, switch, break, continue,
goto, else, etc. They make the language easier even though you could use
other constructs to achieve the same effective result if not the exact code.

Part of the purpose of these implementations is to suggest ideas and examine
them by using them. This allows you to really get an idea of how the code
would look and work in real scenarios. I've written a number of
modifications to the language, some were throwaway, some were 'just seeing
if I can', and others where things I would really like to see change in the
language. Its a hobby and an excercise, and maybe one of my ideas may filter
up and actually affect change. I don't think there is anything really wrong
with that, as long as the idea is a good one and is generally considered a
good one.

Considering I am going to implement this, I obviously support it. However, I
would and do appreciate any comments you have on the syntax as it stands(as
explained in other parts of this thread) as well as on its actual nessecity,
even if they are negative. For what its worth, I do happen to agree with you
that the syntax isn't absolutly vital, as everything I have planned can be
packaged up into a class(with anonymous methods list comprehensions), I just
think that readability, ease of authoring, and the optimization potential
warrents atleast implementing and using the syntax for a while. Hopefully an
implementation wil atleast spark a little more discussion about its merits
and bring in some ground support and clearly inform us that the community as
a whole doesn't like it(as opposed to simply not bothering getting into the
discussion).
 
M

Michael C

Indeed, that was the point.


You'll agree, that foreach (a in 1..100, 200..300) is far better than

foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))

don't you? And exactly that is the purpose of my proposal.
Loops are one of the most common used things and making them more readable
and maintainable should be a desirable goal, don't you agree?

'Better' is a subjective term; and I actually don't agree that it's
'better'. BTW, I'm sure you haven't noticed, but I've been with this thread
from the beginning. In fact, I was the first poster to defend your general
ideas about improving the quality of life for programmers after your ideas
were initially blasted by other posters. That notwithstanding, I have a
couple of questions for you:

I have a sequence 1..100. Is it possible to create a dynamically generated
sequence at run-time using your method like this:

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

Now, assuming I want to specifically exclude 10..20 or 30..40 based on a
user response at run-time, how do I accomplish this using ..?

Now let's say that I want to create a second range at run-time. How can I
do this using ..?

With this second range, expanding on the example above; let's say it's
75..125. Now I want my loop to cover the combination of the two ranges, as
opposed to the single range. How do I do this? I don't want to create a
second foreach loop with a conditional statement attached to do it.

Let's say that I don't know in advance what my second range will be at
design time; only at run-time, but for this particular instance I don't want
the overlapping values counted twice. How can I do this using ..?

Now, as long as we're just adding large numbers of random characters to make
our comparisons look better; i.e.,
foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))

I would say that

foreach (a in Seq(1,100) + Seq(200, 300))

looks far better than

foreach (a in ![{1............................................100,
200............................................300}]!)

Don't you? LOFL

Thanks,
Michael C.
 
M

Michael C

Daniel O'Connell said:
As cody said, the original goal was to make for's easier to write, in
esscense. Its not quite the same because you can't manually advance i, but
still, that was the goal.
The goal grew, for me anyway, to permit the language to succinctly express
lists and some list operations without using explict classes. Much of what I
want to see isn't expressly possible with existing syntax and having to
write a method to do it for each situation is overkill(one method is 10
minutes maybe, but if you have to do it a hundred times a month?). A good
deal of what I'm looking at and driving towards is python\haskell like lists
and list comprehensions.

Generalizing the methodology is important.
Language syntax extension isn't the only acceptable solution(although it is
for the original concept). However I don't think there is any harm in
exploring new syntax that would ease certain concepts in the language. After
all, there are quite a few things you don't *have* to have in the language
that are quite useful, like using, foreach, lock, switch, break, continue,
goto, else, etc. They make the language easier even though you could use
other constructs to achieve the same effective result if not the exact code.

I agree that a lot of what is currently in the language is handy, though not
critical.
Part of the purpose of these implementations is to suggest ideas and examine
them by using them. This allows you to really get an idea of how the code
would look and work in real scenarios. I've written a number of
modifications to the language, some were throwaway, some were 'just seeing
if I can', and others where things I would really like to see change in the
language. Its a hobby and an excercise, and maybe one of my ideas may filter
up and actually affect change. I don't think there is anything really wrong
with that, as long as the idea is a good one and is generally considered a
good one.

Nope, there's nothing wrong with working through theoretical solutions. I
do it all the time myself.
Considering I am going to implement this, I obviously support it. However, I
would and do appreciate any comments you have on the syntax as it stands(as
explained in other parts of this thread) as well as on its actual nessecity,
even if they are negative. For what its worth, I do happen to agree with you
that the syntax isn't absolutly vital, as everything I have planned can be
packaged up into a class(with anonymous methods list comprehensions), I just
think that readability, ease of authoring, and the optimization potential
warrents atleast implementing and using the syntax for a while. Hopefully an
implementation wil atleast spark a little more discussion about its merits
and bring in some ground support and clearly inform us that the community as
a whole doesn't like it(as opposed to simply not bothering getting into the
discussion).

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 appreciate your response.

Cheers,
Michael C.
 
C

cody

Ahh, I thought the point was to be able to create sequences that would
'Better' is a subjective term; and I actually don't agree that it's
'better'. BTW, I'm sure you haven't noticed, but I've been with this thread
from the beginning. In fact, I was the first poster to defend your general
ideas about improving the quality of life for programmers after your ideas
were initially blasted by other posters.


Maybe you've read the thread but you seem not to understand the problem.
I have a sequence 1..100. Is it possible to create a dynamically generated
sequence at run-time using your method like this:

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


Sure. Why should that be a problem.
Now, assuming I want to specifically exclude 10..20 or 30..40 based on a
user response at run-time, how do I accomplish this using ..?


Nobody was talking here about excluding things from a range. It it not
possible
and I do not see very much benefit in it. You can declare a fixed number of
ranges
that might overlap and the arguments are evaluated at runtime.
Additionally we were talking about stating increment values for cases like
datetime
which cannot be incremented simply by ++. Extra syntax would be required and
I don't think it is nessecary to introducing something similar to the very
funny
syntax you just suggested.
I would say that

foreach (a in Seq(1,100) + Seq(200, 300))


What is Seq? If it is a static method it has to be declared in the current
class to use it like that, otherwise you have to state the class name in
front of the method. If the method is nonstatic you have to provide and
object, you know.
One possibility would be to use a ctor which would then look like this
(provided that the class Seq's namespace is imported):

foreach (a in new Seq(1,100) + new Seq(200, 300))

And again, nobody would create a new class and use it everywhere in his
project only to make its loops look better and perform much slower. Would
YOU do that? Surely not.

But if you simply could write foreach (int a in 100..0) wouldn't you used
that instead of for (int i=100; i>=0; i--)? Sure you would.

compare this:

// 3 accesses to the variable in the loop head are redundant and lead to
mistakes:
// It often happens to me that I mix i up with j and similar or
inadvertently use ++ instead of --.
for (int i=w; i>=0; i--)
for (int j=h; j>=0; j--)
{
Foo(i * j);
}

with that:

foreach (i in w..0)
foreach (j in h..0)
{
Foo(i * j);
}

Which one is more readable?
 
C

cody

I agree that a lot of what is currently in the language is handy, though
not
critical.

Why "for", "while" and "switch" if we have goto! Why "string" if we have
char[]?
Why virtual methods if we have delegates to fake them? Why even classes?
Why foreach (a in 0..100) if we already have goto???

It is at the end all just syntactic sugar.

:)
 
C

cody

4) For enumerableExpression, every value is read out *unless* the
The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without breaking
the rules of the language is pretty tough. I've been considering a new
operator, <-, which assigns each value in an enumerable object to a
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.

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 talking optimization of range literals. For the case foreach (int i in
1...1000) I see no reason to generate an object, instead a for loop like you
suggested is generatable. However, for code like
foreach(int i in 1...1000, 1000...1, 2...5 : .5, 3...5838) a generator
object would probably make more sense as it would *vastly* simplify the
output code and probably the break statement as well.


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.
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.

foreach (int a in GetAllPowerOfTwoWherePowerOfTwoIsEven(1, 1000))
{
list.Add(a);
}

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); }}]

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.
But, as I said these are *very* rough and I'm not sure how or if I'll
bother. I do like the concept though and they are quite popular in python.

I'll post more on syntax reasoning if anyone is interested.


Iam very interested in it. Do you have a homepage?
 
M

Michael C

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

As you already know, speed and flexibility are almost inverse to one
another. Since we're putting out our wish list here, to me the best
solution would be one in which all the functionality of a class could be
combined with high-speed optimization technologies. I.e., all ranges that
could be generated statically at compile-time would be, and full-featured
dynamic ranges would be relegated to being created at run-time.

That's all I have to say about that.

Michael C.
 
M

Michael C

cody said:
I agree that a lot of what is currently in the language is handy, though not
critical.

Why "for", "while" and "switch" if we have goto! Why "string" if we have
char[]?
Why virtual methods if we have delegates to fake them? Why even classes?
Why foreach (a in 0..100) if we already have goto???

It is at the end all just syntactic sugar.

:)

(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)

And the MER of this one would be maybe a little bit better:

foreach (a in 0..100, 200..300)

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