1. Есть всем известный цикл For I:=1 to Func(...) do ..., где Func - некоторая функция, выполняющая сложные вычисления. Будет ли эта функция выполняться каждую итерацию цикла, или Delphi сохранит значение этой функции на начало цикла в некоторой скрытой переменной и затем будет сравнивать счетчик именно с ней? То есть стоит ли использовать код, подобный такому:
Temp:=Func(...);
For I:=1 to Temp do ...;
или он будет идентичен вышеприведенному?
2. В справке Delphi сказано:
>>> After the for statement terminates, the value of counter is undefined
Что означает, что значение счетчика после цикла не определено. Но я обычно использую такую конструкцию:
For I:=1 to 10 do ...;
ShowMessage(inttostr(I));
И код показывает значение 11, то есть всегда определенное значение (на 1 больше последнего значения). Я неправильно понимаю текст справки, или в чем дело? Я не хочу, чтобы в один прекрасный день мои программы перестали работать из-за каких-то модификаций Borlandа.
К сожалению, поиск по КС на ключевое слово For дает слишком много ссылок, а как конкретизировать запрос я не знаю.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
26-07-2006 06:59
>>> Или я что-то не понял, или автор той статьи ошибся?
Если посмотреть на страницу внимательно, то в самом верху можно прочитать:
Внимание! В статье имеются неточности и некорректные утверждения.
примечание администрации
Ответить на этот вопрос очень просто: как я уже говорил, надо взять и написать тестовую программу.
Прочитал статью http://www.delphimaster.ru/articles/optimization.html и меня смутила такая фраза:
Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1
Но Границы цикла for вычисляются только один раз - это стандарт языка Паскаль. Или я что-то не понял, или автор той статьи ошибся?
>>> Границы цикла for вычисляются только один раз
Это, однако, хорошо, я сам догадывался, просто не был уверен.
>>> Оптимизатор может изменить код так
Вредная штука этот оптимизатор! И переменные сплошь и рядом удаляет и много чего еще делает. В Ассемблере такого нету. К счастью, а может - к сожалению.
>>> Например, в Perl переменная цикла вообще имеет контекст видимости только внутри цикла
Это сделано зря. ИМХО, конечно.
>>> Вот в си по-моему к переменной после цикла
Это еще кто придумал??? Всегда обращался... Там ведь цикл for(i=0;i<=10;i++)statement; фактически организован как i=0;while(i<=10){statement;i++} и такое ограничение не может иметь места. И грозного предупреждения в Help там тоже нет!
>>> если в цикле нет явных команд обращения к переменной цикла
А для чего тогда цикл? 10 раз вывести Hello, world? А A[I] - это уже адрес. И Си, например, вообще при оптимизации выкидывает индекс и работает с указателями (если я еще помню Си)
>>> используй WHILE или REPEAT-UNTIL
Так и приходится делать. Спасибо. А то я думал, это баг компилятора, а это, оказывается, вроде фичи, если не стандарт. Обсуждение можно считать закрытым.
От себя добавлю, что в весрии с D-5 цикл FOR в 90% случаев превращается в коде в цикл WHILE с декрементом от N до 0. Так что условие окончания цикла он вычисляет один раз - в начале, и уже не проверяет.
Более того, если в цикле нет явных команд обращения к переменной цикла (например, она используется только как индекс массива, но не в вычислениях), то в отладчике просмотреть ее будет очень проблематично.
Простейший пример:
Sum := 0;
for i:=0 to 10
do
Sum := Sum + A[i];
В отладчике на первом шаге имеем I=10, на второй - 9, на последней - 0, хотя на первом шаге извлекается именно A[0], на втором - A[1], и так далее. После цикла переменная i в отладчике вовсе отсутствует.
Может, у вас пример именно так работать и не будет, я его чересчур упростил. Но когда я отлаживал свою прогу - чуть голову не сломал, когда натолкнулся на данный эффект.
Cоздал новый проект, положил на форму кнопку и метку, в обработчике нажатия кнопки написал такой текст:
procedure TForm1.Button1Click(Sender: TObject);
var I,N:Integer;
begin
N:=10;
for I:=1 to N do
begin
if I=1 then
Inc(N);
if I=11 then
Beep;
Label1.Caption:=IntToStr(I)
end;
end;
После нажатия кнопки в Label1 оказывается текст "10", точка останова на Beep не срабатывает. Включение/выключение оптимизации на результат не влияет. На строке Inc(N) компилятор выдаёт подсказку Value assigned to 'N' never used. Эксперимент проделан в Delphi 4, 5, 6 и 7 - везде результат одинаков.
Вот в си по-моему к переменной после цикла и нельзя обратиться.
Клюква. Вы цикл for в С хоть раз видели? Там это вообще разновидность while.
Не знаю, как насчет вычисления только один раз, но в некоторых программах я встречала конструкцию типа
n:=3;
for i:=1 to n do
begin
if i=2 then inc(n);
...
end;
И оно именно что увеличивало границу цикла, т.е. она все-таки вычисляется каждый раз.
А насчет "FOR-Loop variable '<element>' may be undefined after loop", то help общий и для CBuilder'а, и для Delphi. Вот в си по-моему к переменной после цикла и нельзя обратиться.
26-09-2005 07:40 | Комментарий к предыдущим ответам
А так пришлось ограничиться грозным предупреждением в Help.
Ну не только в Help, варнинги тоже исправно выдаются "FOR-Loop variable '<element>' may be undefined after loop".
Присоединяясь за компанию, могу сказать, что обращение к переменной цикла нехорошо также с точки зрения структурного программирования. Например, в Perl переменная цикла вообще имеет контекст видимости только внутри цикла, и снаружи ее просто нет. Если бы в Pascal было понятие переменной контекста блока, наверняка тоже сделали бы именно так. А так пришлось ограничиться грозным предупреждением в Help.
1. Насколько я понимаю, вызов функции будет осуществляться один раз. Но проще написать тестовую прогу и поглядеть (вставить break-point в функцию или глянуть код под отладчиком).
2. Если не хочешь, чтобы прога накрылась, то не пользуйся недокументированными возможностями. В частности, в современных Дельфях (где оптимизирующие компиляторы) цикл может прокручиываться в обратную сторону (так выгоднее). Более того, переменная цикла может быть просто удалена после того, как цикл отработает.
Если тебе нужно значение после завершения цикла, то используй WHILE или REPEAT-UNTIL с самостоятельно выполняемым инкрементом.
1. Границы цикла for вычисляются только один раз - это стандарт языка Паскаль.
2. Оптимизатор может изменить код так, что переменная будет иметь не то значение, которое вы ожидали. И если он сто раз этого не сделал, то не факт, что не сделает и в сто первый.
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.