Delphi: Переменная, используемая в "for In", не может быть назначена! - PullRequest
0 голосов
/ 11 февраля 2010

Почему компилятор запрещает присваивание в цикле for ... in?

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 ]

8 голосов
/ 11 февраля 2010

Это не поддерживается, так как даже простые переменные итератора цикла не могут быть изменены в «нормальном» цикле for. Даже если бы это поддерживалось в for-in , в этом случае это не имело бы особого смысла.

Целые числа являются типами значений, поэтому в каждой итерации цикла все, что должно быть достигнуто, это то, что s будет инициализироваться значением из элемента массива, а затем s будет перезаписано Ct .

Но содержимое массива будет не изменено, и общий эффект кода будет "без изменений".

Чтобы получить то, что вы ожидаете от for-in , вы должны иметь возможность выполнять итерацию с использованием подходящего ссылочного типа (в данном случае PInteger - указатель на целое число), получая ссылается на элементы массива, а не копий значений этих элементов. Новое значение для каждого элемента может быть назначено с помощью разыменованного указателя:

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;

Но не волнуйтесь - это тоже не сработает, это просто демонстрирует природу проблемы, проистекающей из разницы в ссылке и значении.

5 голосов
/ 11 февраля 2010

Я ничего не знаю о Delphi конкретно.Однако большинство языков не позволяют назначать переменную итерации в foreach.Почему вы хотите это сделать?

2 голосов
/ 11 февраля 2010

просто используйте цикл while.

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;
1 голос
/ 12 февраля 2010

Переменная S является просто копией значения в массиве, поэтому ее изменение не будет иметь смысла. Конструкция

for s in ars do

в основном эквивалентно

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

поэтому нет смысла присваивать S. Делайте цикл таким образом

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;
1 голос
/ 12 февраля 2010

Чтобы лучше это понять, я бы сказал, что «понимаем s как управляемый for s в .... construct», то есть пока s контролирует цикл for, хорошо написанный компилятор для почти любой язык заблокирует вас от этого. Любой компилятор, который недостаточно хорошо написан, чтобы блокировать это, должен быть поддержан предупреждением компилятора или инструментом lint, который указывает, что вы делаете что-то, что в лучшем случае ужасно плохого стиля, а в худшем, возможно, приведет к некоторому «неопределенное» поведение, которое было бы трудно предсказать. Что произойдет, если вы установите для s значение, превышающее длину (ars)? Должен ли цикл прерваться или продолжить?

...