Опубликован: 11.02.2005 | Уровень: специалист | Доступ: платный
Лекция 14:

Структурное программирование

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >

О дисциплине циклического структурного программирования

Сейчас сосредоточимся на том варианте структурного программирования, который ориентируется на циклы и массивы.

Прежде всего, нужно остановиться на совместимости структурного циклического программирования с рекурсиями. Опыт показывает, что процедура, в которой есть ее рекурсивный вызов внутри цикла, практически почти всегда ошибочна, теоретически же она выходит за рамки примитивной рекурсии (см. курс логики и теории алгоритмов) и, как следствие, становится практически невычислимой. С тем, чтобы здесь не попасть впросак, соблюдайте простое правило.

Внимание!

Не используйте рекурсивный вызов процедуры внутри цикла! Рекурсия и циклы должны быть "территориально разделены"!

Данное правило пригодно в подавляющем большинстве случаев. Оно является конкретизацией для структурного программирования известного политико-социологического наблюдения:

Самые яростные противоречия возникают либо между двумя близкими сектами, либо при борьбе двух фракций одной и той же секты.

Люди старшего поколения еще помнят, как во время ожесточенной вражды между китайскими и советскими коммунистами китайцы не переставая повторяли, что у них "из десяти пальцев девять - общие".

Тем не менее иногда бывают исключения. Рассмотрим, например, схему поиска вглубь на дереве.

int search (ELEMENT x)
ELEMENT y; int result;
if (good(x)){
   return id(x)}
   else for(int i=0; i<100; i++)
      {y=get_successor(x,i); 
       result=search(y);
       if (result>0) return result;
      }
   return 0;
}
14.4.1.

Здесь рекурсии вместе с циклом задают обход дерева возможностей, и гибельного размножения рекурсивных вызовов не происходит. Причина этого исключения в том, что цикл в данной программе - всего лишь подпорка для рекурсии. В обычном программировании нет функционалов типа mapcar языка LISP, применяющих свой первый аргумент ко всем членам второго10Прямо переписать в языке Common LISP данную программку в рекурсивную с использованием mapcar не удастся, поскольку реализаторы превратили данный функционал в макрос, для того чтобы в стандартных случаях чуть-чуть выиграть в эффективности (многим так не хочется писать тривиальные вещи!). Переопределив mapcar как функцию, можно реализовать рекурсивное определение указанной выше функции search ..

Структурное программирование основано на предположении о локальности действий и условий, поэтому для него в особенности органично подходит иерархическое разбиение задачи на подзадачи - так называемое нисходящее планирование.

При нисходящем планировании начинают с предположения о том, что поставленная задача решена. Затем пытаются реализовать решение общей задачи, выделяя те его блоки, которые оказались неэлементарными, в конструкции следующего уровня. В итоге мы получаем структуру взаимоотношений между конструкциями второго уровня и спецификации на каждую из этих конструкций. Если мы реализуем каждую из конструкций в точном соответствии с полученной спецификацией, то получится решение общей задачи.

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

Нисходящее программирование - конкретизация нисходящего проектирования для нужд программирования. Оно обладает следующими преимуществами.

  • Нисходящее программирование породило хорошо согласованное с ним нисходящее построение структур данных, а нисходящее построение структур данных является простейшим частным случаем глубокой идеальной концепции - абстракции данных. Согласно парадоксу изобретателя, даже простейшие частные случаи высокоуровневых концепций при удачном применении улучшают все характеристики умственных конструкций (в частности, программ).
  • При таком проектировании на каждом уровне можно ограничиваться одной моделью вычислений, в частности операционной, на которую ориентировано структурное программирование.
  • Возможно раннее программирование, когда прототип программы, работающий хотя бы в условиях грубой эмуляции будущих решений низкого уровня, позволяет продемонстрировать, как будет работать полная программа, и улучшить ее пользовательские характеристики.

Ни один конкретный способ анализа либо синтеза не является универсальным. Нисходящее программирование, конечно же, тоже таковым не является. Причины тому, в частности, следующие11Всегда возникает соблазн сделать свой излюбленный способ действий универсальнее, исправив его замеченные недостатки. Но порою достоинства являются продолжением недостатков, и наоборот. При устранении любого частного недостатка, возникшего из-за неуниверсальности системы, появятся другие, как правило, худшие, хотя, может быть, на первых порах менее заметные..

  • Невозможность при нисходящем структурном программировании увидеть тождественность процедур, работающих на разных ветвях декомпозиции, а тем более унифицировать несколько формально различных процедур на разных ветвях в одну общую.
  • Недостаточный учет особенностей, которые могут возникнуть после конкретной реализации запланированных блоков программы, что довольно часто вызывает необходимость полной перепланировки системы после реализации ее блоков, а вслед за такой перепланировкой реализованные для других спецификаций блоки часто оказываются отнюдь не лучшими из возможных.
  • Недостаточный перенос опыта и наработок из одного проекта в другой.
  • В нынешних методиках нисходящего проектирования ко всему перечисленному добавляется еще и навязывание последовательного стиля мышления, что мешает воспользоваться преимуществами нетрадиционных машин.

Это указывает на необходимость средств, позволяющих поднимать уровень понятий. Поэтому в разработке структурных программ применяется также подход, получивший название восходящего программирования. К примеру, когда строят библиотеку, занимаются обобщением задачи. Части, выделяемые в виде библиотечных средств, выбираются таким образом, чтобы они были применимы в различных контекстах.

При создании глобального контекста могут применяться всевозможные стили. Например, модули могут программироваться в стиле автоматного программирования. Если разделение стилей по модулям, пакетам и другим автономным структурным единицам проведено строго, то эклектического их смешения не происходит.

Реальный проект12За исключением мелких. - это смесь нисходящего и восходящего подходов:

  • сначала сверху вниз для выяснения крупных строительных блоков;
  • затем попытка движения снизу вверх, чтобы спроецировать понятия, оформившиеся ранее, на абстрактные структуры, допускающие адекватную реализацию;
  • далее проверка соответствия, углубление нисходящей декомпозиции и обобщение понятий, выделенных восходящими приемами.

Как в нисходящем, так и в восходящем подходе важно обеспечить согласование потоков управления и потоков данных. Основным инструментом здесь может быть взгляд на программу со стороны сетей данных. Поскольку цикл появляется как реализация послойного движения по сети, а массив - как представление слоя сети, то видно, что на самом деле основной информационной структурой цикла является слой сети. Например, в цикле, реализующем числа Фибоначчи, - это структура из fib1 и fib2. Структура, представляющая очередной слой сети, называется реальным параметром (в отличие от формального параметра, диктуемого языком программирования и часто являющегося подпоркой ) цикла. Реальный параметр мы будем называть просто параметром.

Далее, поскольку цикл возникает при повторении однородных действий, а действия диктуются свойствами реальных объектов программы, в цикле должно быть общее свойство, зависящее от параметра и называемое инвариантом цикла. Например, в цикле сортировки - это соотношение между отсортированными и неотсортированными фрагментами массива.

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

Методика циклического и рекурсивного структурного программирования с использованием инвариантов и недетерминированности (но без явного упоминания сетей данных ) прекрасно изложена в учебных пособиях Алагича, Арбиба и Гриса [ 1 ] , [ 9 ] . Настольной книгой программиста должна служить также книга Кормена и др. (учебник MIT) [ 13 ] , в которой можно найти множество хороших примеров того, как находить правильные программные решения и со вкусом использовать подпорки.

Сделаем еще один шаг. Поскольку присваивание с точки зрения решаемой задачи лишь средство задать элементы нового слоя сети, есть смысл считать, что во время всей итерации цикла параметр фиксирован, его изменение происходит лишь в промежутке между двумя итерациями. Таким образом, для согласования потоков данных и потоков управления необходимо сосредоточить все присваивания реальным сущностям в одном месте: либо в конце, либо в начале очередной итерации. Более того, в принципе (и даже не совсем теоретическом, но плохо поддерживаемом современными системами программирования) присваивания можно было бы вообще изгнать, сделав переход старого значения реального параметра цикла к новому неявным, так же, как это сделано для формального, скажем, в языке Pascal.

И, наконец, необходимо заметить следующее.

Внимание!

Призраки и инварианты необходимо осознавать до начала написания текста работающей программы. Если Вы оставите это на потом, то наверняка спутаете подпорки, которые займут у Вас основную часть мысленных ресурсов на завершающем этапе реализации, с сущностями. Более того, восстановить обоснование по тексту уже готовой программы, как ни парадоксально, обычно труднее, чем построить его заранее (то, что восстановить его во всяком случае не легче, было обосновано даже теоретически; смотри последнюю главу книги [ 20 ] ).

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?

Феофан Миков
Феофан Миков
Россия, Томск, ТПУ, 2013
Виталий Лусников
Виталий Лусников
Россия, Киров, ВятГГУ, 2001