views:

240

answers:

5

Why is the assignment in the for...in loop disallowed by the compiler?

procedure TForm1.Button1Click(Sender: TObject);
Var
  ars : Array [0..10] of Integer;
  s : Integer;
  ct : Integer;
begin
  ct := 0;
  for s in ars do
  Begin
    s := ct; // Does not compile!
    Inc(ct);
  End;
End;
+5  A: 

I know nothing about Delphi specifically. However, most languages don't allow you to assign to the iteration variable in a foreach. Why do you want to do this?

JSBangs
This is also the case in Delphi. http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements#For_Statements
Bruce McGee
I know its not allowed, but the given use case ist perfectly valid.
Uwe Raabe
-1. Does not answer the question.
Rob Kennedy
@Uwe - the use case isn't valid at all (in the sense that it can possibly hope to achieve what the poster is asking for), at least not without a radical change in the nature of the type system in Delphi.Even if the code as written were allowed to compile, it's a no-op. The effect would be to change the value of a temporary variable, NOT modify the contents of the collection being iterated over (which must be the posters intent as the code posted has no discernible purpose otherwise).
Deltics
As I said, I know that it is not allowed to change the iterator variable, but the use case may be for instance: "Iterate over an array of elements and set each element to a fixed value". In this case the type of the array index doesn't bother me at all, so a for-in-loop looks very attractive. You have to know, that in a for-in-loop the loop variable is only read-only or a copy of the corresponding array element, to see that this won't work. A look into some of the enumerators in the VCL could give you this insight.
Uwe Raabe
+2  A: 

just use a while loop instead.

procedure TForm1.Button1Click(Sender: TObject);
Var
  ars : Array [0..10] of Integer;
  i : Integer;
  ct : Integer;
begin
  ct := 0;
  i := 0;
  while i < Length(ars) do
  Begin
    ars[i] := Ct; //Does Compile!
    Inc(ct);
    inc(i);
  End;
End;
JamesB
Why do you prefer a while loop over a simple for i:=0 to Length(ars)-1 do loop? The problem was not the for-loop, but the for-in-loop.
Uwe Raabe
-1. Does not answer the question.
Rob Kennedy
@Rob Kennedy There wasn't a question when I posted this originally.
JamesB
@Uwe Raabe Depends what they want to do with the loop as you can't assign for loop variables either. without a more specific context a while loop is more flexible.
JamesB
@James, I don't see how that's a defense at all. How can you possibly post a useful answer when, by your own admission, there was nothing to answer?
Rob Kennedy
@Rob Kennedy I just assumed his question was "How do I do this with code that compiles" As is normally the case when code is posted with "This doesnt compile".
JamesB
+6  A: 

This is not supported, just as even simple loop iterator variables cannot be modified in a "normal" for loop. Even if this were supported in a for-in, it would not make much sense in this case.

Integers are value types, so in each iteration of the loop all that would be achieved is that s would be initialised to a value from an element the array and then s overwritten by Ct.

But the array contents would not be modified and the net effect of the code would be "no change".

To get what you expect from a for-in you would have to be able to iterate using a suitable reference type (in this case a PInteger - pointer to integer) yielding references to the array elements, rather than copies of the values of those elements. A new value for each element could then be assigned using the dereferenced pointer:

var
  ars : array [0..10] of Integer;
  s : PInteger;
  ct : Integer;
begin
  ct := 0;
  for s in ars do  // << this WON'T yield pointers to the array elements ..
  begin
    s^ := Ct;      // .. but if it did you could then write this
    Inc(ct);
  end;
end;

But don't get excited - this won't work either, it merely demonstrates the nature of the problem stemming from the difference in a reference vs a value.

Deltics
+1  A: 

To understand this better, I would say, "understand s as being controlled by the for s in .... construct", that is to say, while s is in control of the for loop, a well written compiler for almost any language will block you from doing this. Any compiler that is not well enough written to block this, should be backed up by a compiler warning, or a lint-tool that indicates you are doing something that is at best, terribly bad style, and at worst, perhaps will lead to some "undefined" behavior that would be hard to predict. What happens if you set s to a value that is higher than the Length(ars)? Should the loop abort, or should it continue?

Warren P
I think you're confusing this loop with an ordinary Delphi `for` loop. It's a `for-in` loop, where `s` is a *value* from the array, not an *index*. Were it an index, we might be tempted to set `s` to a big value to make the loop stop running, but here, it's apparent that Sylvain wanted to change the value stored in the array, as though `s` were a reference or alias rather than the copy it really is.
Rob Kennedy
In an ordinary delphi for loop you COULD change it right? But that would still be very bad style. I would expect code metrics or LINT tools to at least point that fact out to me.
Warren P
No, you can't change the loop variable in any `for` loop. My point was that even if assigning to `s` in this loop *were* allowed, it wouldn't have any effect on how the loop ran because it doesn't actually control the loop in any way.
Rob Kennedy
In fact I had an array of records that i wanted to initialize each element wthi a specific value.
Sylvain L.
+1  A: 

The variable S is just a copy of the value in the array, so changing it would have no meaning. The construct

for s in ars do

is basically equivalent to

for i := low(ars) to high(ars) do
  s := ars[i]

so there's no point assigning to S. Do the loop this way

procedure TForm1.Button1Click(Sender: TObject);
Var
  ars : Array [0..10] of Integer;
  i : Integer;
  ct : Integer;
begin
  ct := 0;
  for i := low(ars) to high(ars) do
  Begin
    ars[i] := ct;
    Inc(ct);
  End;
End;
Rob McDonell