|
Я это делаю Персональное меню Голосование Поиск по сайту Реклама
Статистика |
Пусть дан массив из n целочисленных элементов
Пусть про этот массив известно, что один из элементов равен x. Требуется найти этот элемент и записать его позицию в целую переменную i. Типичный начинающий программист (и многие «профессионалы») сразу напишет FOR c вложенной проверкой и начнет искать способ выйти из цикла:
Но в Обероне/Компонентном Паскале из цикла FOR выйти просто так нельзя. Если не отказываться от цикла FOR (см. ниже решение этой задачи с циклом WHILE), то единственный способ это сделать — заключить цикл в процедуру и использовать оператор RETURN, позволяющий выйти из процедуры в произвольной точке:
Тогда в основной программе вместо цикла нужно вызвать эту процедуру:
Еще можно заменить FOR на оператор LOOP, в теле которого допускается выход на конец цикла (оператор EXIT). Но ни в том, ни в другом случае нельзя «выпрыгнуть» вообще в произвольную точку вне цикла: RETURN «прыгает» на конец процедуры, а EXIT — на конец цикла. Таким образом программа остается достаточно структурированной. Кстати, вот классическое «правильное» решение в стиле Дейкстры, выраженное на Обероне/Компонентном Паскале:
Напомним, что вычисление логических выражений в Обероне/Компонентном Паскале производится по правилу «короткого замыкания»: если результат логической операции можно определить по первому операнду, то второй не вычисляется. В данном случае по достижении i = n первый операнд (i < n) даст FALSE, так что результатом операции & (логическое И) тоже будет FALSE, и выход из цикла произойдет сразу, без вычисления второго операнда. Заметим, что вычисление второго операнда привело бы к ошибке и аварийной остановке программы из-за выхода за верхнюю границу массива. Правило весьма удобно при систематической разработке алгоритмов и приводит к лаконичным программам; одно это правило исключает множество ситуаций, где наивный инстинкт требует использовать GOTO. Много примеров на этот счет — в том числе гораздо менее тривиальных, чем приведенный — дано в книгах Гриса и Дейкстры в нашем списке. Заметим еще, что по приведенной стандартной схеме решается большое число задач, сводящихся к задаче поиска:
Логическое значение, выражающее успех поиска, равно значению выражения <условие ограничения поиска> после выхода из цикла. В некоторых языках, например В Си, можно выйти и из цикла for:
Смотрите также
Оригинальная идея этого примера: Последняя модификация: 13.11.05 18:28 Обсуждение статьи в форумеНе проходите мимо! Оставьте Ваш комментарий в форуме! >>> 22.11.05 11:43 Screamer В примере поиска элемента массива на самом деле избавления от флага нет. В данном случае на выходе должно быть два результата - найден ли элемент и где он найден. И решается этот вопрос так, что если результат имеет смысл (как индекс массива), то это индекс массива, а если не имеет смысла (отрицательных индексов массива не бывает) - то элемент не найден. В php можно изменить тип результата на boolean, но в типизированных языках такое не прокатывает. 22.11.05 13:12 popoff
Любую программу можно рассмореть с разных точек зрения. О программе, работающей с числами, всегда можно сказать, что там есть флаг - знак плюс или минус. Сам по себе флаг не является чем-то плохим. Плохо - это когда флаги используют в местах, где они не нужны и лишь затрудняют понимание. Это как комментарии в программах. О них принято считать, что комментарии - это всегда хорошо. Но на самом деле комментарии используют, когда код плохой. Если код написан хорошо, то для того, чтобы в нем разобраться, не нужны комментарии. 22.11.05 16:11 Screamer Да, но нужно понимать, особенно начинающим программистам, используется ли флаг в процедуре (но при этом совмещен с другими данными) или не используется вообще. Иначе, напрмер, можно, забыв обработать флаг «элемент массива не найден», получить ошибку «попытка доступа к несуществующему элементу массива». Или не учесть, что значение флага на самом деле допустимо и как валидный результат - к примеру, если в массиве допускаются и отрицательные индексы. 28.11.05 21:27 popoff Я, кажется, понял 1. Плохой является перегрузка значений переменных, когда в одной переменной хранится несколько разных по смыслу данных. Как в нашем случае, в одной переменной хранится одновременно и индекс в массиве и флаг, который говорит о том, найден ли результат. Что хуже, перегрузка значений или введение флагов, пока что сложно сказать. 2. В нашем случае флаг «найден ли элемент в массиве» является результатом работы функции поиска и как бы является не совсем тем флагом, о которых я поднял вопрос в данном разделе статей. Я поднял вопрос о тех флагах, которые нужны для того, и только для того, чтобы управлять ходом работы программы, и сами по себе не являются некоторым осмысленным результатом или исходным данным. Если вопрос о том, «найден ли элемент в массиве» приписывать к рассматриваемым мной флагам, то я должен был бы говорить не о флагах, а о булевских данных вообще. Вероятно, стоило бы ввести раздел с определением терминов. 3. В формулировке задачи написана совершенно конкретная фраза:
Согласно постановке задачи, значение результата при отсутствующем элементе не определено. Хотя, это - не правильно. Все значения должны быть определенными, и проверки на правильность значений должны выполняться всегда, когда значения переменных формируются (условно) не в предыдущей строчке программы. 4. В случае с процедурой, если элемент не найден, то переменная Возможно, проверка на существование выполняется отдельно от поиска этого значения. 5. В исходном варианте при использовании цикла while в случае, если элемент не найден, в цикл записывается значение, выходящее за пределы индексов в массиве, равное n. Это можно рассматривать как флаг «элемент не найден». Если сравнивать два способа перегрузки значений переменных, то вероятно, предложенная мной в варианте на Си перегрузка значений лучше, чем предложенная в примере на Обероне, потому что “-1” явно прописана в программе, а “n” определяется из логики работы; значение “-1” является понятным для всех массивов, о которых известно, что индекс не может быть отрицательным и для того, чтобы понять, найден ли элемент, не нужен сам массив; если используется “n”, то для того, чтобы понять, найден ли элемент, нужно знать, в дополнение к результату поиска, как минимум длину массива или даже весь массив, если, как в PHP, в качестве индексов допускается что-угодно (в том числе и отрицательные числа). Но если допустить в качестве индексов что-угодно, то и алгоритм поиска будет совсем другим. 6. Три приведенных варианта решения задачи поиска элемента в массиве не являются эквивалентными. В первом варианте значение переменной не изменяется, во втором - возвращается значение n, а в третьем - значение -1. 7. Вариант с циклом, в котором перебираются все значения - это плохой код, потому что он не оптимален - перебираются все значения, когда можно перебирать меньше. Вариант с процедурой - это лучше, и, возможно, даже, намного лучше, потому что начинающие часто забывают выносить повторяющийся и/или обособленный (решающий отдельную, со своим ограниченным набором входных данных и результатов, независимую от остальных задачу) код в процедуры. В варианте с процедурой я плохим назвал бы использование процедуры вместо функции (я не знаю Оберона и не знаю, почему в оригинале использовалась процедура - возможно, там нет функций, хотя это весьма сомнительно; возможно также, такой вариант был приведен умышленно, чтобы показать описываемые мной в текущем пункте недостатки). Я назвал бы плохим подход с использованием изменяемых аргументов - от них хорошо бы избавляться везде, где от них можно избавиться. И я назвал бы плохим использование внешних переменных (в процедуре внешними являются массив, в которым производится поиск и само значение, которое ищется). Это становится понятным, если спросить у любого, кто не знает, как устроена процедура «Найти», что означает такой код:
Лично я подумал бы, что нужно найти значение, хранящееся в переменной i. Но при этом я задался бы вопросом, «а где искать?» и «а почему игнорируется результат поиска?». Этот код не говорит сам за себя и требует обязательного комментария. Код, который требует комментария, является плохим кодом. 8. Исходя из задачи, поднятой в рассматриваемом примере («поиск элемента в массиве»), недостатки кода, рассмотренные в п.7, не имеют к задаче никакого отношения. Однако могут быть использованы при комментировании варианта решения с процедурой как дополнительный показатель того, что человек, который решил задачу таким способом, на самом деле решил задачу плохо. 9. Наша цель - показать, какой код является хорошим, а какой - плохим. При этом «хорошим» является код, работу которого можно понять без дополнительных комментариев, о котором нельзя сказать, что в нем есть какие-то «хитросплетения логики». Сравнивая два варианта, об одном можно сказать, что он лучше, чем другой, если работу первого можно понять быстрее, чем работу другого, если внести изменения в первый можно быстрее, чем во второй и если при внесении изменений в первый вариант вероятность возникновения ошибки меньше, чем при внесении изменений во второй вариант. 10. Для достижения нашей цели не требуется приводить хороший вариант решения. Достаточно привести плохой вариант и показать, чем он плох. Хороший вариант - это вариант, который не обладает рассмотренными недостатками. Приводить «хороший вариант» - это всегда опасно. Потому что для большинства существующих программ можно показать, чем они плохи и как их можно было бы улучшить. Наша цель - не найти абсолютно хороший вариант, а предложить приемлемый вариант. Можно легко показать, что «абсолютно хорошего варианта» не существует. 11. Исходя из нашей цели (п.9), не имеет значения, можно ли написать вариант решения с процедурой, который не обладает недостатками, приведенными в п.7. Мы привели плохой вариант и показали, чем он плох. Мы добились нашей цели. 12. Исходя из нашей цели (п.9), нам становится все равно, каким именно образом обрабатывается ситуация «элемент не найден». Мы привели вариант плохого решения и обратили внимание на то, чем этот вариант является плохим. Мы добились нашей цели. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30.01.10 10:37 VNik62a Чрезвычайно рад встрече на сайте после некоторого перерыва. Нахожу его чрезвычайно для себя полезным и желаю автору развития и процветания 05.04.10 23:28 Hale_32bit Спасибо автору. Хорошие темы здесь поднимаются. Просмотреть все комментарии в режиме форума. Всего комментариев: 6
|