Тема открыта по просьбе жителей Королевства и посвящена обсуждению вопросов оптимизации кода. Выставляйте свои лучшие и худшие тексты и не стесняйтесь их обсуждать. В споре рождается истина. Или, по крайней мере, оптимизация.
Всего в теме 737 сообщений
Добавить свое сообщение
Отслеживать это обсуждение
- Тестирование проекта. Отладка.
- Подводные камни
- Централизованная обработка ошибок
- Бета-тестирование
- Давайте учиться на ошибках.
- Почему программисты допускают ошибки?
- Автоматизированные тесты для GUI
- О системах контроля ошибок
737—688 | 687—638 | ...>>> Всего сообщений в теме: 737; страниц: 15; текущая страница: 1
№ 737 14-05-2012 01:53 | |
№ 736 14-05-2012 00:48 | |
Привет всем. Есть проблемка напиал программу по обработке текстового файла. Но есть 2 проблемки.
1. обработка файла начинается по событию botton.click. Но как форма становится не активной(напрмер я захожу в мой компьютер и т.п.) то программа сразу зависает и отказывается дальше работать.Файл с которым я работаю 37мб более 2 миллионов строк. Как этого исбежать???
2. Очень долго исправляется файл. В нем примерно 47000 ошибок. и всеэто растягивается на часов 13... Открываю с помощью Tstinglist потом построчно делаю поиск и если нахожу ошибку то исправляю сохраняю в файл.
№ 735 24-03-2012 18:03 | |
Ответ на »сообщение 733« (Германн)
___________________________
ОК, на Королевстве иногда хочется почитать не обсуждения каких-то конкретных задач, а более общих рассуждений о разных вопросах, связанных с программированием.
Полный ответ готовлю, но сегодня уже не успею
Да, торопиться-то особо некуда, это же не "помогите, к понедельнику курсовой сдавать надо!" ))
Сергей О.
Завтра, максимум послезавтра, отвечу. Точнее "продолжу обсуждение не вопроса, но темы".
№ 734 24-03-2012 00:30 | |
Ответ на »сообщение 733« (Германн)
___________________________
ОК, на Королевстве иногда хочется почитать не обсуждения каких-то конкретных задач, а более общих рассуждений о разных вопросах, связанных с программированием.
Полный ответ готовлю, но сегодня уже не успею
Да, торопиться-то особо некуда, это же не "помогите, к понедельнику курсовой сдавать надо!" ))
№ 733 23-03-2012 18:43 | |
Прочитал.
С очень многим соглашусь. Но не со всеми вашими утверждениями.
Подробный ответ требует времени. Пока только скажу, что вы немного "передернули". Моё первое возражение было на ваш совет, что "по хорошему" автору следует использовать доппотоки. А вопрос о выборе асинхронного режима как предпочитаемого появился уже позже.
P.S.
Полный ответ готовлю, но сегодня уже не успею.
№ 732 23-03-2012 04:31 | |
№ 731 23-03-2012 04:25 | |
Что я имею:
-Процессор отнюдь не занят, как предполагал Alexeyslav - программа занимает около 0% процессорного времени.
-Чтение "в штатном режиме" длится ровно столько, за сколько придут заказанное кол-во байтов в порт (пренебрегая накладными расходами).
- Если ошибка чтения (например коммутатор не подключен к компьютеру), то функция возвратится через 200 мс. У меня например в этой задаче состояние коммутатора опрашивается раз в 1 сек. Поскольку чтение происходит в основном потоке, то в ситуации ошибок обмена (коммутатор не подключен и т.п.) примерно 200 мс из секунды программа "висит" и это заметно, например когда её перетаскиваешь по рабочему столу - небольшие скачки, но не создает реальных неудобств. Я считаю, что в нерабочем режиме это вполне допустимо. Нормальный режим программы - когда она подключена к коммутатору и обмен осуществляется нормально.
- Код и его логика простые до неприличия.
Не вижу причин, почему в этой ситуации я должен был бы перейти на асинхронное чтение, где вместо 1 вызова одной функции вызывать
ReadFile, WaitCommEvent, WaitForSingleObject, читать по 1 байту 4 раза (вызывая после приема каждого байта опять функциии ожидания) и потом эти 4 байта складывать в 1 буфер.
Повторюсь, ситуация - специфическая и конкретная, особенности я описал. Но в моей практике подавляющее число задач именно такие. В других случаях чтение вынесено в отдельный поток, но так же происходит синхронно. Но я думаю, что такого рода задач и с другими железками наверное не так уж мало.
Я несколько раз собирался в каких-то задачах перейти на асинхронное чтение, как более идеологически правильное, как "вот если вдруг ситуация изменится и твое синхронное чтение уже не подойдет", но каждый раз в конце концов останавливался на более простом варианте, описанном здесь. Если будет какие-то другие задачи, где приведенный подход будет решать их плохо, приводить к проблемам, тогда наверное придется использовать асинхронный режим.
Для Alexeyslav, попробуйте эксперимент: откройте порт, задайте время чтения большим, секунд 10 (ReadTotalTimeoutConstant := 10000) и вызовите синхронно ReadFile. Если ничего к порту не подключено, то естественно ничего не прочитается, и функция возвратится после конца таймаута. Программа на эти 10 сек зависнет. Но посмотрите с помощью диспетчера задач нагрузку на процессор - она будет 0%. 100% вы получаете из-за того, что постоянно в цикле вызываете ReadFile, т.е. делаете этот самый поллинг, который Василий здесь не раз ругал. Я решаю задачу иначе, выставляя размер буфера чтения в соответствии с задачей
и выставляя таймауты не "от балды", как написал Германн, а исходя из временных параметров протокола обмена.
Еще раз повторю тезис.
Насчет "синхрона". В каких-то конкретных задачах синхронное чтение вполне нормально решает задачу и решает ее простым способом.
Насчет потоков: и при синхронном и при асинхронном чтении в общем случае стоит использовать дополнительный поток, хотя в каких-то случаях можно обойтись и без этого.
№ 730 23-03-2012 04:18 | |
Ответ на »сообщение 729« (Сергей О.)
___________________________
в сообщение попал мусор. Вот этот отрывок без форматирования как код:
then Result := True
else EscapeCommFunction(HCom, CLRRTS);
end;
не нужен.
№ 729 23-03-2012 04:16 | |
Я не писал, что синхронное чтение хорошо всегда, а написал "простота такого подхода, если он решает задачу". Правильно применение синхронного чтения, по крайней мере в моем опыте, требует правильного задания как раз тех самых таймаутов. Опишу тип задач, которые часто встречаются мне, в управлении изделиями нашей фирмы. Устройство - slave, ждет запроса или команды от компьютера (программы), получив его, отвечает в заданное время (в общем-то - сразу же). Часто длина пакета в обе стороны - не более ~20 байт. Опыт показывает, что ответ (точно не помню. весь обмен туда-сюда или только время ответа, давно измерял) приходит через 60-80 мс. Иногда из-за разных виндовских штучек задержка может быть и немного больше 100 мс.
Как я делаю? В ряде давно написанных программ, которые я поддерживаю и сейчас - один поток. Пишу в порт функцией WriteFile сразу весь пакет, например
type
TPacket=packed record
Header: byte;
Command: byte;
Data1: byte;
Data2: byte;
Data3: byte;
CtrlSum: byte;
end;
...
var
Packet: TPacket;
function SendCommand: Boolean;
var WrRslt: LongBool;
begin
Result := False;
BytesWritten := 0;
if not EscapeCommFunction(HCom, SETRTS)
then Exit;
WrRslt := WriteFile(HCom, Packet, SizeOf(Packet), BytesWritten, nil);
if WrRslt and (BytesWritten = SizeOf(Packet))
then Result := True
else EscapeCommFunction(HCom, CLRRTS);
end;
Для чтения задаю таймауты так:
ReadTotalTimeoutMultiplier:=0;
ReadTotalTimeoutConstant:=200;
Т.е. функция чтения возвратит результат или когда получит то, что хочет, или через 200 мс, если ответ не получит в силу каких-то причин (например коммутатор не подключен к порту). 200 мс с запасом превышает ожидаемое время ответа, т.е. если за 200 мс полный ответ не пришел, значит произошла ошибка. Читаю так, например одна из команд, опрос состояния одного из каналов:
type
TStateAnswer = packed array [1..4] of byte;
then Result := True
else EscapeCommFunction(HCom, CLRRTS);
end;
Для чтения задаю таймауты так:
ReadTotalTimeoutMultiplier:=0;
ReadTotalTimeoutConstant:=200;
Вот пример, где читается 4 байта, в других ситуациях с этим же подходом бывает и 1, и 33 байта например (передача 33 байта на скорости 9600 порядка 30 мс).
type
TStateAnswer=packed array [1..4] of byte;
...
var
StateAnswer: TStateAnswer;
...
ReadSuccess := ReadFile(HCom, StateAnswer, SizeOf(StateAnswer), BytesRead, nil);
продолж. следует
№ 728 23-03-2012 04:13 | |
К вопросам оптимизации кода это имеет наверное косвенное отношение, но создавать темы на КД самому невозможно, эта мне кажется более-менее подходящей. Для удобства разобью текст на несколько сообщений.
Тема - про синхронное чтение/запись в Com-порт. В ответ на мои слова
В-третьих никакой причины работать синхронно с изначально асинхронным СОМ-портом нет и быть не может. - Есть причина для этого - простота такого
подхода, если он решает задачу.
Василий написал:
нет ничего хуже, чем в Виндах применять чисто ДОС-овский синхрон. Причем, этот чудик Билли развивая свое детище все больше и больше делает поблажки
любителям синхрона.
Германн написал:
Вот эта простота как раз та, которая хуже воровства.
Alexeyslav написал:
Синхронный режим хорош своей простотой, но все достоинства тут же и заканчиваются.
Дальше у Alexeyslav были слова про то, что такое чтение тормозит и загружает процессор на 100%, про поллинг (опрос порта каждую 1 мс) и т.д.
Германн написал про таймауты "от балды".
Сначала, ответ на реплику Германна про потоки: когда я написал об отдельном потоке, я не подразумевал именно синхронный режим чтения. Вы сами согласились, что функцию WaitCommEvent стоит вызывать в отдельном потоке. MSDN пишет и Василий в своей статье
http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1126
приводит эти слова про вызов этой функции: "при неудаче и при GetLastError=ERROR_IO_PENDING для получения маски эвентов необходимо вначале вызвать Wait-функцию (например WaitForSingleObject) на ожидание установки hEvent структуры lpOver (параметр WaitCommEvent ) в сигнальное состояние.". О чем я и написал, что используются функции WaitForxxx. Насколько я знаю нередко делают так (Василий в своей статье имено так делает): выносят в отдельный поток вообще операцию чтения вместе с функциями ожидания. Т.е. и при синхронном и при асинхронном чтении частно используют дополнительный поток (потоки).
Теперь по поводу синхронного чтения из порта.
Напомню, что синхронное чтение, это когда мы вызываем функцию ReadFile и ждем пока из порта не прочитается заданное в функции кол-во байт или же не выполнится заданное нами условие таймаута чтения. Асинхронное стение - когда функция возвращает результат сразу, а затем мы ожидаем оповещения о том, что что-то прочитано.
продолж. следует.
№ 727 06-02-2012 04:09 | |
В принципе, поэкспериментировав с пересылкой данных, через eax,mm0,xmm0 пришел к выводу, что передача данных упирается в скорость шины памяти, и от выравнивания мало что зависит, т.к. при обработке массива, даже если он не выровнен, то "лишние" байты закачанные в кэш, все равно потребуются при следующей операции чтения.
Использование mm0, кстати, дает 15-20% ускорения по сравнению с eax. Использование xmm0 уже не дает ничего, что с выравниваем, что без выравнивания, отсюда и написанный выше вывод.
Однако, по существу вопроса - оптимизация нужна и использование MMX и SSE2.
На Круглом столе, к сожалению, вопросов про эти наоборы команд ничтожно мало, неужели никто не оптимизирует.
Как там с поддержкой AVX и регистров YMM в Delphi XE, никто не знает?
№ 726 03-02-2012 06:25 | |
Ответ на »сообщение 724« (fargo2)
___________________________
Косвенно к вопросу оптимизации. Касается обмена данными с процессором
Недавно опытным путем выяснил, что при включенном ALIGN=8, Delphi 7 не желает выравнивать содержимое массивов типа array of byte по границе 8.
Интересно, появился ли в новых версиях Delphi принудительный ALIGN=16 и воздействует ли он на array of byte?
Директива ALIGN задаёт максимальное значение выравнивания. Каждый тип (как встроенный, так и определённый программистом) имеет собственный Align. Для байтов и массивов байтов Align=1, поэтому директива ALIGN на их выравнивание влиять не должна.
ЗЫЖ Сто лет сюда не заходил, заглянул - никого...
№ 725 27-01-2012 05:07 | |
Естественно, имеется ввиду первый элемент массива!,
а не каждый элемент в массиве.
№ 724 24-01-2012 01:15 | |
Косвенно к вопросу оптимизации. Касается обмена данными с процессором
Недавно опытным путем выяснил, что при включенном ALIGN=8, Delphi 7 не желает выравнивать содержимое массивов типа array of byte по границе 8.
Интересно, появился ли в новых версиях Delphi принудительный ALIGN=16 и воздействует ли он на array of byte?
№ 723 25-11-2009 06:17 | |
всем доброго дня)Подскажите, пожалуйста, где можно прочитать про (Производительность ПО.Методики измерения производительности .Программные измерительные мониторы)..долго и упорно искал в интернете..но нашел слишком мало информации...пожалуйста помогите))
№ 722 16-06-2009 02:40 | |
№ 721 16-06-2009 02:00 | |
Ответ на »сообщение 720« (Игорь)
___________________________
Вопросы задаются на Круглом Столе, а не здесь.
№ 720 16-06-2009 01:29 | |
Здравствутйе.
В делфи есть иконка Timage существует ли механизм маштабирования нарисованноно изображения
Подскажите
Заранее благодарю
№ 719 27-05-2009 00:09 | |
>>>ПРОЦЕДУРА Оболочка;
>>>НАЧАЛО
>>>ВЫЗОВ ПРОЦЕДУРЫ Имя;
>>>КОНЕЦ;
>>>Итого имеем один вход и один выход.
Можно и без оболочки. Обычно от альтернативных выходов можно избавиться с помощью другой формы записи условной конструкции.
Например, "неправильную" процедуру типа:
ПРОЦЕДУРА Имя;
НАЧАЛО
ЕСЛИ условие ТО ВЫХОД
что-то...
КОНЕЦ
можно переписать так:
ПРОЦЕДУРА Имя;
НАЧАЛО
ЕСЛИ НЕ условие ТО
что-то...
КОНЕЦ ЕСЛИ
КОНЕЦ
>>>Какое же это ветвление?
>>>Это выполнение по условию.
Игра словами. Ветвление - это структура в записи алгоритма, которая обеспечивает выбор и выполнение операций в зависимости от результата проверки определенных условий.
№ 718 25-05-2009 06:27 | |
Ответ на »сообщение 717« (Как слышно? Прием!)
___________________________
Это смотря какие теоретики :)
Первый серьезный язык, моделирующий паралельные процессы, - СИМУЛа 67 имел операторы Activate, Passivate. Сохранялась точка алгоритма, из которой управление передавалось другому процессу. В нее происходил возврат при активации. Если прочертить на тексте стрелками все передачи управления между процессами, получим знакомую картину "спагетти". Activate, Passivate - аналоги GOTO для параллельных процессов.
Достаточно очевидно, что ничего, не описываемого моделью конечного автомата все равно не реализовать при таком подходе. И моделирование параллельных процессов перешло к аналогу структурного программирования - ансамблю конечных автоматов, обменивающихся сигналами.
Тогда речь шла о моделировании параллельных процессов на одном исполнителе, но идея управляющего списка легко модифицируется на произвольное число исполнителей.
№ 717 25-05-2009 04:01 | |
Не помню где недавно вычитал примерно следующее.
При упоминании распределённых вычислений
теоретики правильного программирования впадают в панику.
№ 716 25-05-2009 03:58 | |
Ответ на »сообщение 713« (S.)
___________________________
ПРОЦЕДУРА Имя;
НАЧАЛО
...
Получаем управляющие данные от другого модуля (ещё один вход)
...
ЕСЛИ что-то проверяем ТО выходим из процедуры
что-то делаем
...
КОНЕЦ;
2 входа, 2 выхода.
Оболочка только очерчивает группу переходов.
№ 715 25-05-2009 03:51 | |
Ответ на »сообщение 713« (S.)
___________________________
>>> ***Точка входа в конструкцию ветвления
>>> ЕСЛИ условие ТО алгоритм1 ИНАЧЕ алгоритм2
>>> ***Точка выхода из конструкции ветвления
Какое же это ветвление?
Это выполнение по условию.
То есть можно заменить на последовательное.
ЕСЛИ условие ТО алгоритм1
ЕСЛИ НЕусловие ТО алгоритм2
Ветвление, это когда переходим на разные логические потоки
со своими дальнейшими вариантами ветвления в том числе.
Другое дело, что так называемое структурное программирование
с принципом разработки "сверху вниз" действительно подразумевает
последовательное, цепочечное выполнение программы.
Именно от этого устойчивого заблуждения
появилась упрощенческая формула "один вход, один выход".
Если вспомнить про параллельное программирование,
то подобная "структура" с очевидностью выморочна.
№ 714 25-05-2009 02:30 | |
Ответ на »сообщение 713« (S.)
___________________________
>>> На этом обсуждение оптимизации кода можно заканчивать :)
:D
Знаете, обычно я на ассемблер перехожу, когда что-то не получается эффективно реализовать на уровне ЯВУ. В частности, я очень люблю машинные команды для обработки строк, и меня дико раздражает, что при компиляции программы на Паскале вместо этих команд используется перебор элементов массива.
>>> Всегда?
Да, всегда. Сделаем Вашу процедуру внутренней и для нее допишем процедуру-оболочку
ПРОЦЕДУРА Оболочка;
НАЧАЛО
ВЫЗОВ ПРОЦЕДУРЫ Имя;
КОНЕЦ;
Итого имеем один вход и один выход.
Я ведь сразу уточнил, что всегда относится к ситуации, "Если же речь идет о реализации" ;-)
№ 713 24-05-2009 23:03 | |
>>>Функция достаточно эффективна. Размер -- 17 байт.
На этом обсуждение оптимизации кода можно заканчивать :). Из двух конструкций на языке высокого уровня выбираем тот, для которого транслятор генерирует более короткий и быстродействующий код.
>>>Ветвление не проходит под формулу "один вход, один выход".
Если под ветвлением понимать только одно условие, то да. А если всю конструкцию, то нет.
***Точка входа в конструкцию ветвления
ЕСЛИ условие ТО алгоритм1 ИНАЧЕ алгоритм2
***Точка выхода из конструкции ветвления
Впрочем у меня сейчас нет под рукой работ "классиков" по этому вопросу. Что они сами имели в виду утверждать пока не могу. Надо поискать первоисточники...
>>>Если же речь идет о реализации, то тут проблем никаких: упаковываем
>>>реализацию алгоритма в процедуру и всегда получаем один вход и один
>>>выход ;-)
Всегда?
;-)
ПРОЦЕДУРА Имя;
НАЧАЛО
что-то делаем
...
ЕСЛИ что-то проверяем ТО выходим из процедуры
что-то делаем
...
выходим из процедуры
КОНЕЦ;
Здесь 1 вход, но 2 выхода!
№ 712 22-05-2009 07:25 | |
Ответ на »сообщение 687« (S.)
___________________________
Я все же поразвлекался дома и сделал реализацию на ассемблере
function FindPos(FindVal : Byte; BufPtr : Pointer; BufLen : Integer) : Integer; register;
asm
push edi
mov edi,edx
mov edx,ecx
repne scasb
je @Found
xor edx,edx
@Found:
mov eax,edx
sub eax,ecx
pop edi
end;
Здесь FindVal -- значение, которое нкужно найти (тип -- Byte). BufPtr -- указатель на начало буфера данных. BufLen -- длина буфера. Возвращает позицию байта в буфере, имеющего значение равное FindVal. Нумерация начинается с единицы. Если элемент не найден, возвращается ноль.
Функция достаточно эффективна. Размер -- 17 байт. И работать будет быстрее, чем предложенные варианты на Паскале, так как оптимизатор ни доля одного варианта не предлагает поиск через REPNE SCAS, а занимается последовательным перебором элементов массива, сравнивая каждый с искомым значеним.
№ 711 21-05-2009 12:06 | |
Ответ на »сообщение 705« (S.)
___________________________
>>> у структурного алгоритма должна быть 1 точка входа и 1 точка выхода
У Дейкстры было последовательное выполнение, цикл и ветвление.
Ветвление не проходит под формулу "один вход, один выход".
Либо Вы имели в виду что-то другое.
№ 710 21-05-2009 07:49 | |
Ответ на »сообщение 700« (Александр Алексеев)
___________________________
Цикл for тоже можно исключить из грамматики языка "без какого-либо ущерба".
Ага. в Python так и сделали. Есть только for-in и while.
№ 709 21-05-2009 06:28 | |
Ответ на »сообщение 701« (S.)
___________________________
>>> А моделировать for с помощью while или repeat - это неудобно
Думаю, что программисты на Си не поймут данного утверждения. Преимущество цикла FOR в Паскале -- скорость работы. Недостаток -- ограничение на использование переменной цикла. Ну и еще к недостаткам можно отнести, что в цикле WHILE нужно инкремент переменной самростоятельно выполнять, но зато шаг цикла можно делать произвольным. Вот и все различия, которые я смог придумать. А в остальном, что вариант
for i:=1 to N do
begin
end;
что вариант
i:=1;
while i <= N do
begin
Inc(i);
end;
да хотя бы и вариант
for(i=0;i<=N;i++)
все это -- вещи одного порядка, и особой сложности в переходе с одного на другой не наблюдается.
Ответ на »сообщение 705« (S.)
___________________________
>>> А какое отношение у общественности к такому принципу структурного программирования: "у структурного алгоритма должна быть 1 точка входа и 1 точка выхода"?
Не понял, причем тут это. Ну, ладно. Про вход согласен. А вот с выходами -- не совсем. Если рассуждать на уроыне алгоритма, то иногда для полнимания удобнее остановитья на нескольких выходах (например, правильный и ошибочный). Единственно, нужно четко выделять терминальные элементы алгоритма.
Если же речь идет о реализации, то тут проблем никаких: упаковываем реализацию алгоритма в процедуру и всегда получаем один вход и один выход ;-)
№ 708 21-05-2009 06:14 | |
№ 707 21-05-2009 05:45 | |
№ 706 21-05-2009 05:26 | |
>>>Ломание копий по поводу GOTO в этой теме уже было
Проклятый склероз! :) Конечно, мы уже общались на эту тему. Причем очень конструктивно. Только тогда меня еще звали "JKJ" и я был еще не такой старый :))).
№ 705 21-05-2009 05:18 | |
>>>Так что ничего плохого в этом нет.
А какое отношение у общественности к такому принципу структурного программирования: "у структурного алгоритма должна быть 1 точка входа и 1 точка выхода"?
№ 704 21-05-2009 05:14 | |
>>>При использовании Break и Continue мы продолжаем работать с циклом
>>>(как структурным элементом написания программы), а не с отдельными
>>>строками, соответственно, остаемся на уровне струкутрного
>>>программирования. Так что ничего плохого в этом нет.
У меня тоже нет никакого абсолютного отрицания break. Я примерно так и сказал - не люблю, но без фанатизма :))
№ 703 21-05-2009 05:07 | |
Ответ на »сообщение 700« (Александр Алексеев)
___________________________
>>> goto - это атавизм, который можно исключить из грамматики языка без ущерба для качества программ
Цикл for тоже можно исключить из грамматики языка "без какого-либо ущерба".
goto и for - это совершенно разные вещи. goto делает листинг не читаемым, а циклы легко сворачиваются в современных текстовых редакторах, тем самым декомпозиция происходит более легко.
Мне кажется что в основу языковых конструкций должны быть заложены основные принципы программирования: декомпозиция, объединение, иерархия и научные методы
№ 702 21-05-2009 05:02 | |
Ответ на »сообщение 699« (S.)
___________________________
Ломание копий по поводу GOTO в этой теме уже было (см. начиная с »сообщение 122« ).
Кто-то из этого спора что-то вынес, кто-то остался при своем. В общем же, смысл такой: GOTO страшен не потому, что выполняется безусловный переход. Вредность GOTO в том, что, используя его, мы начинаем работать на уровне отдельных операторов (строк). В то время как структурное программирование (начиная с которого декларируется вредность GOTO) предполагает работу с более крупными конструкциями (блоки операторов, процедуры, классы). Таким образом, с точки зрения структурного программиоования, процедуры Break и Continue для циклов (а также процедура Exit для подпрограмм) не являюится плохими: это не произвольный переход куда попало, а структурный элемент цикла (ну, или подпрограммы, если речь идет об Exit). При использовании Break и Continue мы продолжаем работать с циклом (как структурным элементом написания программы), а не с отдельными строками, соответственно, остаемся на уровне струкутрного программирования. Так что ничего плохого в этом нет.
№ 701 21-05-2009 04:55 | |
>>>Цикл for тоже можно исключить из грамматики
>>>языка "без какого-либо ущерба".
Разумеется! У Вирта, если не ошибаюсь, даже была такая попытка в первых версиях Оберона. Потом вернули. Ущерб в данном случае возникает не для программы, а для программиста. Без goto можно обходиться легко. А моделировать for с помощью while или repeat - это неудобно.
№ 700 21-05-2009 04:46 | |
>>> goto - это атавизм, который можно исключить из грамматики языка без ущерба для качества программ
Цикл for тоже можно исключить из грамматики языка "без какого-либо ущерба".
№ 699 21-05-2009 04:22 | |
>>>А вообще-то я вреден для желудка: слишком желчный и противный. Я тоже могу
>>>в ответ поднять бучу за использование чего-нибудь.
Между прочим, мы затронули очень интересную тему, тесно связанную с проблемой качества и безопасности программирования.
Что такое "Принципы программирования" и как к ним вообще следует относиться? Как к "абсолюту", который "выстрадан" теорией и практикой программирования и которому надо следовать всегда и везде? Или как к рекомендации, которой можно пожертвовать в пользу какого-то другого критерия, например, ради быстродействия загрузочного кода?
Типичный пример такой "религиозной" войны - это отношение к оператору goto. Одни программисты вообще не используют эту конструкцию, даже при ее наличии в языке. Другие используют, но только иногда и ради повышения быстродействия кода (читаемость исходного текста в данном случае уходит на второй план). Третьи вообще считают goto обычной конструкцией, не более вредной, чем любая другая и с Дейкстрой в этом вопросе не соглашаются.
У меня в связи с этим свой личны опыт. Примерно 5 лет мне пришлось писать на СУБД-ориентированных языках типа dBASE, FoxBase, FoxPro, Clipper. А там никакого goto не было (во всяком случае в той документации, которой я пользовался). Поэтому у меня даже не возникало такого вопроса - использовать goto или нет. Все проекты, которые у нас шли, получались без goto просто автоматически. И после того, как примерно 50 тысяч строк исходников были написаны без безусловных переходов и без каких-либо проблем с реализацией алгоритма, я поверил Дейкстре окончательно. goto - это атавизм, который можно исключить из грамматики языка без ущерба для качества программ. С тех пор - это у меня как рефлекс - писать все, что угодно без goto.
С break ситуация сложнее. Просто это по смыслу очень напоминает goto, только с переходом без метки и только вперед, за границу тела цикла. Поэтому с этой конструкцией отношения "особые". Без особой необходимости стараюсь не употреблять, но не доводя дело до "фанатизма".
№ 698 21-05-2009 02:14 | |
Ответ на »сообщение 697« (Денис Зайцев)
___________________________
>>> Ща придёт Илья Ермаков и всех вас зохавает за использование Break :)
Даже если Вас "зохавали", у Вас все равно есть два выхода :D
А вообще-то я вреден для желудка: слишком желчный и противный. Я тоже могу в ответ поднять бучу за использование чего-нибудь.
№ 697 21-05-2009 02:08 | |
Ответ на »сообщение 690« (Бел Амор)
___________________________
Оба варианта чётки и интуитивны. При прочих равных условиях предпочитаю цикл for...
Ответ на »сообщение 691« (Geo)
___________________________
...Но при этом упорно не желаете пользоваться процедурой Break...
Ща придёт Илья Ермаков и всех вас зохавает за использование Break :)
№ 696 21-05-2009 02:02 | |
Ответ на »сообщение 695« (S.)
___________________________
>>> Так будет быстрее выполняться? Несмотря на обращение к функции?
Насколько я помню, процедура Inc не является процедурой в чистом виде. Это то, что принято называть compiler magic: синтаксически мы пишем процедуру, но компилятор для этой процедуры тела не создает, подставляя в соответствующее место определенный код.
№ 695 21-05-2009 01:57 | |
>>>Оба варианта чётки и интуитивны. При прочих равных условиях предпочитаю
>>>цикл for как более интуитивный и более ошибкобезопасный.
Спасибо. Я так не написал по чисто "субъективной" причине. У меня "аллергия" на две вещи: на оператор goto и на досрочный выход из цикла (break). В свое время заразился и вот до сих пор болею :)
>>>1. Вместо i:=i+1 используйте Inc(i)
Так будет быстрее выполняться? Несмотря на обращение к функции? Или компилятор развертывает это в один и тот же код? Если так, то почему лучше?
№ 694 21-05-2009 01:57 | |
Ответ на »сообщение 689« (Cepгей Poщин)
___________________________
>>> речь о том, что в одном языке используется стиль характерный для другого, выглядит это несколько некрасиво
Поскольку это все же больше мне, то я и отвечу. А что мне делать, если есть определенный моменты, которые плохо реализованы в Паскале, но хорошо -- в Си? Ждать, пока кто-то напишет синтезированный язык? Написать его самому? Ну его на фиг. Лучше буду писать, как пишу.
№ 693 21-05-2009 01:54 | |
Ответ на »сообщение 688« (S.)
___________________________
>>> Какой цикл будет "оптимальнее"?
Оптимальнее будет тот цикл, в котором при проверке сравнение выполняется с нулем ;-) Оптимальность не зависит от того, в каком направлении выполняется перебор. Например, такие циклы будут по оптимальности одинаковы
for i:=1 to n do
for i:=n downto 1 do
А с другой стороны, вот такой цикл
for i:=-10 to 0 do
будет эффективнее, чем
for i:=0 downto -10 do
№ 692 21-05-2009 01:50 | |
Добавлю от себя.
Мне если честно, все эти новые конструкции языка появляющиеся год от года, даются если честно с трудом, наверно потому, что могу спокойно обходится без них. Возьмем тот же for in... Но кому то это нравится и он во всю пользуется. Другое дело оптимизация кода, стоит сказать что есть решения языка, есть решения алгоритмичесткие. В С больше вывертов с оптимизацией записи кода, но и проблем согласитесь всетаки тоже поболее, да и разбирать код написанный так, особено чужой тяжело. В Delphi тоже в год от года вносятся модные нововведения, например Operator Overloading или анонимные методы, которые по моему мнению сделаны как то не совсем прозрачно и читабельно, особенно для программиста иследующего код. Есть полезные Class Helper. Есть не очень полезные Nested Clases - по моему мнению лучше делать два класса отдельно, да и читать такую конструкцию тяжело
type TOuterClass = class
strict private
myField: Integer;
public
type
TInnerClass = class
public myInnerField: Integer;
procedure innerProc;
end;
procedure outerProc;
end;
procedure TOuterClass.TInnerClass.innerProc;
begin ...
end;
пока немного кода еще ничего, а если он разрастется. Потом мы забываем, что оптимизация кода вещь двоякая, часть оптимизации может взять на себя компилятор, часть программист. Если оптимизируем для компа - это одно, а если оптимизируем код для передачи например заказчику - это другое. Второе подразумевает наверно более структурный и системообразующий подход, включающий написание документации, внесения комментарий и т.п. Поэтому оптимизация оптимизации рознь. Или вспомнив поговорку лучшее-враг хорошего нужно искать золотую середину.
Вот так сумбурно как то...
№ 691 21-05-2009 01:47 | |
Ответ на »сообщение 687« (S.)
___________________________
Метод 1 вовсе не дурацкий. Ну, разве что нет смысла выполнять цикл до конца, если искомый элемент найден. Ах, да... Вам же нужно вхождение первого элемента, а не последнего, так что цикл нужно выполнять в прямом направлении, а не в обратном. То есть лучше сделать rfr-nj так:
с:=0;
for i:=1 to n do
if a[i]=0
then
begin
с:=i;
Break;
end;
Метод 2.
>>> Неплохо, но при отсутствии нулей проверка второго условия за границами массива будет исключена только при условии сокращенного вычисления булевых выражений, т.е. корректность написанного текста зависит от настроек компилятора. Насколько это грамотно?
Если Вы пишете текст, который может компилироваться неизвестно кем, то вставьте в текст опцию компилятора {$B-}.
Метод 3 хуже, потому что от него никаким опциями компиляитора не защитишься.
В методе 4 вы постепенно доходите до того, что я озвучил в модификации метода 1. Но при этом упорно не желаете пользоваться процедурой Break.
while i <= n do
if a[i] = 0 then Break else Inc(i);
if i > n
then
else
Мне больше нравится либо второй вариант (с выставленной опцией компилятора, если в этом есть потребность), либо четвертый. Хотя в спешке могу и модифицированный первый использовать.
Ну, а если, говоря про оптимальность, подразумевать скорость, то последовательный перебор элементов массива не является опитмальным. Правильнее будет использовать специальные команды процессора, обрабатывающие строки. Либо функции языка программирования, использующие эти команды. Например, если речь идет о поиске символа в строке, то разуимнее использовать функцию Pos, которая окажется быстрее, чем последовательны перебор символов. Если нужно для массивов, то можно и на асме функцию накатать, используя REP SCAS. Пример без проверки писать не хочу, так как могу допустить ошибку (плохо помню особенность работы этой команды, а писать надежно, но неэффективно на асме не хочется).
№ 690 21-05-2009 01:41 | |
Ответ на »сообщение 687« (S.)
___________________________
Метод 1 (самый "дурацкий")
с:=0;
for i:=n downto 1 do
if a[i]=0 then с:=i
Действительно дурацкий, поскольку:
1. Перебирается весь массив без необходимости.
2. Происходит многократное присвоение целевой переменной.
3. Цикл развёрнут, что скрывает смысл.
Метод 2.
i:=1;
while (i<=n) and (a[i]<>0) do i:=i+1
Неплохо, но при отсутствии нулей проверка второго условия за границами массива будет исключена только при условии сокращенного вычисления булевых выражений, т.е. корректность написанного текста зависит от настроек компилятора. Насколько это грамотно?
Есть любители применения быстрой оценки. Лично я избегаю таких моментов всеми силами и второе условие всегда оформляю через then if
Лично я так не написал бы никогда.
Метод 3.
i:=1;
while (a[i]<>0) and (i<=n) do i:=i+1
Это уже не грамотно по указанной выше причине. При отсутствии нулей проверка первого условия будет некорректна после выхода i за границу массива.
Сами всё сказали...
Метод 4.
c:=0; i:=1;
while (c=0) and (i<=n) do
begin
if a[i]=0 then c:=i;
i:=i+1
end
Самый "длинный", но похоже самый "умный" из четырех.
Завязка на конкретное начальное значение и последующее сравнение на неравенство ему выглядит крайне некрасиво. Кроме того, крайне неинтуитивно...
Самое интересное в этих примерах то, что все они "работают". Но что будет "оптимальным"? Особенно с точки зрения обучения "правильному и эффективному программированию"?
В таких случаях я вспоминаю загадку:
Вопрос: Чем отличаются программа и самолёт?
Ответ: Некрасивый самолёт не полетит...
Я бы предложил два других варианта:
c := 1;
while c <= n do
if a[c] = 0 then
Break
else
Inc(c);
for i := 1 to n do
if a[i] = 0 then
begin
c := i;
Break;
end; Оба варианта чётки и интуитивны. При прочих равных условиях предпочитаю цикл for как более интуитивный и более ошибкобезопасный. Только нужно хорошо запомнить, что за пределами цикла переменную цикла использовать нельзя.
P.S.
1. Вместо i:=i+1 используйте Inc(i)
2. Используйте кнопку CODE для подсветки синтаксиса (здесь).
3. Форматируйте код (вообще).
№ 689 21-05-2009 01:06 | |
Ответ на »сообщение 684« (S.)
___________________________
Вас S, похоже за живое задело :)
В самой идее "смешивания" языков в рамках одного проекта нет ничего страшного. Речь не о том, что часть программы написана на одном языке, часть на другом. Давайте из мира идей вернемся к теме обсуждения »сообщение 673«, мой наезд на C был конечно шуткой, но если серьёзно, то речь о том, что в одном языке используется стиль характерный для другого, выглядит это несколько некрасиво, как разговор с сильным акцентом. И главное не надо делать из этого глубоких выводов, а то опять во флейме потоним.
№ 688 21-05-2009 00:14 | |
И еще один "детский" вопрос "в догонку".
Какой цикл будет "оптимальнее"?
for i:=0 to n do
или
for i:=n downto 0 do
737—688 | 687—638 | ...>>> Всего сообщений в теме: 737; страниц: 15; текущая страница: 1
Добавить свое сообщение
Отслеживать это обсуждение
Дополнительная навигация: |
|