Delphi : Variable used in the “for In” cannot be a

2019-09-04 22:39发布

问题:

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;

回答1:

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.



回答2:

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?



回答3:

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;


回答4:

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?



回答5:

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;