Циклы for и while, операторы break и continue, волшебное слово else

В этой статье я расскажу о циклах for и while, операторах break и continue, а также о слове else, которое, будучи употребленное с циклом, может сделать программный код несколько более понятным.
Цикл while
While — один из самых универсальных циклов в Python, поэтому довольно медленный. Выполняет тело цикла до тех пор, пока условие цикла истинно.
Цикл for уже чуточку сложнее, чуть менее универсальный, но выполняется гораздо быстрее цикла while. Этот цикл проходится по любому итерируемому объекту (например строке или списку), и во время каждого прохода выполняет тело цикла.
Оператор continue
Оператор continue начинает следующий проход цикла, минуя оставшееся тело цикла (for или while)
Оператор break
Оператор break досрочно прерывает цикл.
Слово else, примененное в цикле for или while, проверяет, был ли произведен выход из цикла инструкцией break, или же «естественным» образом. Блок инструкций внутри else выполнится только в том случае, если выход из цикла произошел без помощи break.
Для вставки кода на Python в комментарий заключайте его в теги
- Модуль csv - чтение и запись CSV файлов
- Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
- Онлайн-обучение Python: сравнение популярных программ
- Книги о Python
- GUI (графический интерфейс пользователя)
- Курсы Python
- Модули
- Новости мира Python
- NumPy
- Обработка данных
- Основы программирования
- Примеры программ
- Типы данных в Python
- Видео
- Python для Web
- Работа для Python-программистов
- Сделай свой вклад в развитие сайта!
- Самоучитель Python
- Карта сайта
- Отзывы на книги по Python
- Реклама на сайте
Цикл For или While
Это не критично для системы. Для компилятора - это примерно один и тот же набор команд. Тут скорее вопрос в логике и читаемости кода. Хотелось бы увидеть фрагмент кода. И тогда можно решить какой цикл предпочтителен для данной конкретной ситуации.
15 июл 2015 в 17:34
Во-первых, они одинаковы, а во-вторых, джава - это немного не тот язык, для которого имеет смысл спрашивать о подобных вещах. Или имелся в виду foreach?
15 июл 2015 в 17:42
Кто за закрытие голосовал - почему??
15 июл 2015 в 17:42
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Вы опоздали с вашим вопросом лет эдак на 30-40.
Сейчас никакой разницы нет, вообще. Компиляторы умные, и если смысл кода по сути один и тот же, обычно производят одинаковый байткод.
Кроме того, даже если бы производительность разных видов циклов и отличалась, расходы времени на управление циклами составляют порядка нескольких наносекунд. «Оптимизация» такого толка вам ничего не принесёт: расходы на запуск приложения (чтение его из файловой системы) на несколько порядков больше.
Преждевременная оптимизация — источник всех бед. Оптимизируйте лучше алгоритмы и структуры данных. И не забывайте о читаемости кода, она в миллионы раз важнее потенциального выигрыша в одну процессорную инструкцию.
Что быстрее while (true) или for (;;)?
Поскольку каждый защищал “свой вечный цикл” как родного, я решил разобраться. Кто же пишет более оптимальный код.
Я написал 2 исходника:
#include int main (int argc, char* argv[]) < while(1)< printf("1\n"); >>
#include int main (int argc, char* argv[]) < for(;;)< printf("1\n"); >>
$ gcc -O3 while.c -o while.o3 $ gcc -O2 while.c -o while.o2 $ gcc -O1 while.c -o while.o1 $ gcc -O3 for.c -o for.o3 $ gcc -O2 for.c -o for.o2 $ gcc -O1 for.c -o for.o1
И дезассемблировал. Кому лень читать ассемблерные листниги — можете прокрутить страницу вниз. Собственно листинги:
$ objdump -d ./while.o3 . 0000000000400430 : 400430: 48 83 ec 08 sub $0x8,%rsp 400434: 0f 1f 40 00 nopl 0x0(%rax) 400438: bf d4 05 40 00 mov $0x4005d4,%edi 40043d: e8 be ff ff ff callq 400400 400442: eb f4 jmp 400438 . $ objdump -d ./while.o2 . 0000000000400430 : 400430: 48 83 ec 08 sub $0x8,%rsp 400434: 0f 1f 40 00 nopl 0x0(%rax) 400438: bf d4 05 40 00 mov $0x4005d4,%edi 40043d: e8 be ff ff ff callq 400400 400442: eb f4 jmp 400438 . $ objdump -d ./while.o1 . 000000000040051c : 40051c: 48 83 ec 08 sub $0x8,%rsp 400520: bf d4 05 40 00 mov $0x4005d4,%edi 400525: e8 d6 fe ff ff callq 400400 40052a: eb f4 jmp 400520 . $ objdump -d ./for.o1 . 000000000040051c : 40051c: 48 83 ec 08 sub $0x8,%rsp 400520: bf d4 05 40 00 mov $0x4005d4,%edi 400525: e8 d6 fe ff ff callq 400400 40052a: eb f4 jmp 400520 . $ objdump -d ./for.o2 . 0000000000400430 : 400430: 48 83 ec 08 sub $0x8,%rsp 400434: 0f 1f 40 00 nopl 0x0(%rax) 400438: bf d4 05 40 00 mov $0x4005d4,%edi 40043d: e8 be ff ff ff callq 400400 400442: eb f4 jmp 400438 . $ objdump -d ./for.o3 0000000000400430 : 400430: 48 83 ec 08 sub $0x8,%rsp 400434: 0f 1f 40 00 nopl 0x0(%rax) 400438: bf d4 05 40 00 mov $0x4005d4,%edi 40043d: e8 be ff ff ff callq 400400 400442: eb f4 jmp 400438
Разбираем на пальцах
Различные оптимизации не повлияли на реализацию цикла while (true) — он всегда выполнял 3 команды: mov, callq и jmp. Так же оптимизации не повлияли на реализацию for — он тоже всегда был из 3х команд: mov, callq, jmp. Между собой mov, callq и jmp ничем не отличались. Длинна команд в байтах во всех 6и случаях неизменна.
Есть только небольшая разница между реализациями -O1 и -O2/-O3 jmp выполнялся на main+4 а не на main+8, но с учетом того, что это статичный адрес (как видно из asm-кода) оно тоже не несет разницы в производительности… Хотя… а вдруг страницы памяти разные, ведь на сколько я знаю для телодвижений между разными страницами памяти в x86 (и amd64) требуются дополнительные усилия проца!
Узнаем:
400438/4096 = 97,763183594
400520/4096 = 97,783203125
Пронесло. Страница памяти одна. Да это 97 страница Виртуальной памяти Виртуального адресного пространства процесса. Но именно она нам и нужна.
Итог
while (true) и for (;;) идентичны по производительности между собой и с любыми оптимизациями -Ox. Так что если Вас спросят кто из них быстрее — смело говорите что “for (;;)” — 8 символов написать быстрее, чем “while (true)” — 12 символов.
Для тех, кто не верит что без -Ox будет тоже самое:
$ gcc while.c -o while.noO $ objdump -d while.noO . 40052b: bf e4 05 40 00 mov $0x4005e4,%edi 400530: e8 cb fe ff ff callq 400400 400535: eb f4 jmp 40052b . $ gcc for.c -o for.noO $ objdump -d for.noO . 40052b: bf e4 05 40 00 mov $0x4005e4,%edi 400530: e8 cb fe ff ff callq 400400 400535: eb f4 jmp 40052b .
P.S. конечно все это будет правдой на компиляторе “gcc version 4.7.2 (Debian 4.7.2-5)”
while vs foreach vs for что быстрее в php
Один из самых важных и "узких" моментов, скорость работы массива. По массиву меню в процессе построения функции могут пробегать по многу раз, соответственно крайне важно чтоб этот момент работал на максимуме возможностей. давайте разберёмся всю ли правду пишут нам в интернете, какой массив работает быстрее while vs foreach vs for. Часть тестов не несут осмысленной нагрузки - ради интереса добавлены. Каждый тест перезапускался несколько раз.
Необходимо понимать, что тест имеет перекос в сторону обращения к двумерному массиву.
$a=array( 0=>array(0=>'top') ,1=>array(0=>'top') ,2=>array(0=>'top') ,3=>array(0=>'top') ,4=>array(0=>'top') ,5=>array(0=>'left') ,6=>array(0=>'left') ,7=>array(0=>'left') ,8=>array(0=>'left') ,9=>array(0=>'left') ); в функциях function f1() > 2. foreach($a as $k => $v) < if($v[0]=='right')<>> 3. for($i=0; $i > 4. $c=count($a); for($i=0; $i > 5. $c=count($a)-1; for($i=$c; $i>0; --$i) < if($a[$i][0]=='right')<>> 6. reset($a); while(list($k, $v) = each($a)) < if($v[0]=='right')<>> 7. reset($a); while(list(, $v) = each($a)) < if($v[0]=='right')<>> 8. $i=0; while($i++$i; > 9. $i=0; $c=count($a); while($i <$c)< if($a[$i][0]=='right')<>++$i; > 10. $i=count($a)-1; while($i>-1) < if($a[$i][0]=='right')<>--$i; > 11. $i=count($a)-1; while($i>=0) < if($a[$i][0]=='right')<>--$i; >
Результаты тестирования скорости массивов в PHP
| № | вызовов / функция | время | быстрее на | |
|---|---|---|---|---|
| 1 | 1 / foreach($a as $v) | 0.000018 | +0.000006 с. | +33.33 % |
| 2 | 1 / foreach($a as $k => $v) | 0.000012 | 0.000000 с. | 0 % |
| 3 | 1 / for($i=0; $i | 0.000036 | +0.000024 с. | +66.67 % |
| 4 | 1 / $c=count($a); for($i=0; $i | 0.000014 | +0.000002 с. | +14.29 % |
| 5 | 1 / $c=count($a)-1; for($i=$c; $i>0; --$i) | 0.000022 | +0.000010 с. | +45.45 % |
| 6 | 1 / reset($a); while(list($k, $v) = each($a)) | 0.000029 | +0.000017 с. | +58.62 % |
| 7 | 1 / reset($a); while(list(, $v) = each($a)) | 0.000035 | +0.000023 с. | +65.71 % |
| 8 | 1 / $i=0; while($i| 0.000028 |
+0.000016 с. |
+57.14 % |
|
| 9 | 1 / $i=0; $c=count($a); while($i<$c) | 0.000021 | +0.000009 с. | +42.86 % |
| 10 | 1 / $i=count($a)-1; while($i>-1) | 0.000013 | +0.000001 с. | +7.69 % |
| 11 | 1 / $i=count($a)-1; while($i>=0) | 0.000021 | +0.000009 с. | +42.86 % |
| № | вызовов / функция | время | быстрее на | |
|---|---|---|---|---|
| 1 | 1 / foreach($a as $v) | 0.000014 | +0.000005 с. | +35.71 % |
| 2 | 1 / foreach($a as $k => $v) | 0.000009 | 0.000000 с. | 0 % |
| 3 | 1 / for($i=0; $i | 0.000035 | +0.000026 с. | +74.29 % |
| 4 | 1 / $c=count($a); for($i=0; $i | 0.000012 | +0.000003 с. | +25 % |
| 5 | 1 / $c=count($a)-1; for($i=$c; $i>0; --$i) | 0.000020 | +0.000011 с. | +55 % |
| 6 | 1 / reset($a); while(list($k, $v) = each($a)) | 0.000023 | +0.000014 с. | +60.87 % |
| 7 | 1 / reset($a); while(list(, $v) = each($a)) | 0.000036 | +0.000027 с. | +75 % |
| 8 | 1 / $i=0; while($i| 0.000026 |
+0.000017 с. |
+65.38 % |
|
| 9 | 1 / $i=0; $c=count($a); while($i<$c) | 0.000021 | +0.000012 с. | +57.14 % |
| 10 | 1 / $i=count($a)-1; while($i>-1) | 0.000012 | +0.000003 с. | +25 % |
| 11 | 1 / $i=count($a)-1; while($i>=0) | 0.000023 | +0.000014 с. | +60.87 % |
В порядке возрастания
| № | вызовов / функция | время | быстрее на | |
|---|---|---|---|---|
| 2 | 1 / foreach($a as $k => $v) | 0.000012 | 0.000000 с. | 0 % |
| 10 | 1 / $i=count($a)-1; while($i>-1) | 0.000013 | +0.000001 с. | +7.69 % |
| 4 | 1 / $c=count($a); for($i=0; $i | 0.000014 | +0.000002 с. | +14.29 % |
| 1 | 1 / foreach($a as $v) | 0.000018 | +0.000006 с. | +33.33 % |
| 9 | 1 / $i=0; $c=count($a); while($i<$c) | 0.000021 | +0.000009 с. | +42.86 % |
| 11 | 1 / $i=count($a)-1; while($i>=0) | 0.000021 | +0.000009 с. | +42.86 % |
| 5 | 1 / $c=count($a)-1; for($i=$c; $i>0; --$i) | 0.000022 | +0.000010 с. | +45.45 % |
| 8 | 1 / $i=0; while($i| 0.000028 |
+0.000016 с. |
+57.14 % |
|
| 6 | 1 / reset($a); while(list($k, $v) = each($a)) | 0.000029 | +0.000017 с. | +58.62 % |
| 7 | 1 / reset($a); while(list(, $v) = each($a)) | 0.000035 | +0.000023 с. | +65.71 % |
| 3 | 1 / for($i=0; $i | 0.000036 | +0.000024 с. | +66.67 % |
| № | вызовов / функция | время | быстрее на | |
|---|---|---|---|---|
| 2 | 1 / foreach($a as $k => $v) | 0.000009 | 0.000000 с. | 0 % |
| 10 | 1 / $i=count($a)-1; while($i>-1) | 0.000012 | +0.000003 с. | +25 % |
| 4 | 1 / $c=count($a); for($i=0; $i | 0.000012 | +0.000003 с. | +25 % |
| 1 | 1 / foreach($a as $v) | 0.000014 | +0.000005 с. | +35.71 % |
| 5 | 1 / $c=count($a)-1; for($i=$c; $i>0; --$i) | 0.000020 | +0.000011 с. | +55 % |
| 9 | 1 / $i=0; $c=count($a); while($i<$c) | 0.000021 | +0.000012 с. | +57.14 % |
| 6 | 1 / reset($a); while(list($k, $v) = each($a)) | 0.000023 | +0.000014 с. | +60.87 % |
| 11 | 1 / $i=count($a)-1; while($i>=0) | 0.000023 | +0.000014 с. | +60.87 % |
| 8 | 1 / $i=0; while($i| 0.000026 |
+0.000017 с. |
+65.38 % |
|
| 3 | 1 / for($i=0; $i | 0.000035 | +0.000026 с. | +74.29 % |
| 7 | 1 / reset($a); while(list(, $v) = each($a)) | 0.000036 | +0.000027 с. | +75 % |
Выводы
- Неожиданно foreach показал хороший результат..
- Не все циклы в обратном порядке работают быстрей.
- Count внутри цикла работает медленнее, везде об этом пишут - вычисляется на каждом шагу значение.
- Любые конструкции где php приходиться самому додумывать, обрабатывать пропуски - медленнее, так и здесь while(list(, $v)
- Больше или равно работает медленнее нежели меньше, поэтому >-1 писать выгоднее нежели >=0 итп.
- foreach($a as $k=>$v) быстрее чем foreach($a as $v)
Много где пишут, что foreach медленнее остальных циклов, при этом тесты проводят только через foreach($a as $v). Отдельно в следующий раз протестирую что же быстрей for, while на каком-нить синтетическом тесте без обращения к массиву.
Меню раздела "Тесты производительности php"
Страница сгенерирована за 0.001694 сек.
На один процесс веб-сервера: 590 стр./сек.
Всего Apache может отдать: 151 040 стр./сек.
Выделено PHP памяти: 434.29 KB
(real_usage: 2 MB)
Браузер построил за сек.
Полное время сек.
С момента выгрузки предыдущей страницы
из памяти браузера: сек.