Наше мышление основано в первую очередь на зрительном восприятии. Вадим Глезер |
Попробуем включить воображаемый “боковой прожектор” и взглянуть на проблему под другим углом зрения. Существует некоторое, причем весьма глубокое, хотя и не всегда очевидное сходство между изложенными выше идеями и концепцией структурного программирования. Исходя из этого, введем термин “визуальное структурное программирование” и определим его как набор правил, совпадающий с визуальным синтаксисом языка ДРАКОН. В концентрированном виде эти правила изложены в гл. 15.
Чтобы отграничить теоретические аспекты визуального структурного программирования от второстепенных деталей, нам понадобится термин “шампур-метод”. Впрочем, иногда выражения “шампур-метод” и “визуальное структурное программирование” будут использоваться как синонимы.
Размышляя над проблемой, автор пришел к следующим предварительным выводам или, лучше сказать, предположениям.
В данной главе сделана попытка обосновать заявленные выводы.
Согласно классической теореме Бома и Джакопини, всякая реальная программа может быть построена из функциональных блоков (действий) и двух конструкций: цикла и дихотомического выбора (развилки). Эдсгер Дейкстра обогатил и усилил эту идею, предложив отказаться от оператора безусловного перехода goto и ограничиться тремя управляющими конструкциями: последовательность, цикл, выбор.
Дональд Кнут подверг критике тезис Дейкстры о полном исключении goto, продемонстрировав случаи, где goto полезен. В итоге возникла плодотворная дискуссия, строго говоря, не завершенная до сих пор, в ходе которой выявились четыре варианта мнений (табл. 4).
Позиция участников дискуссии | Используются три структурные конструкции? | Используются заменители goto? | Используются goto? |
Вариант 1 | Да | Нет | Нет |
Вариант 2 | Да | Нет | Да |
Вариант 3 | Да | Да | Да |
Вариант 4 | Да | Да | Нет |
Вариант 1 описывает ортодоксальную позицию Дейкстры, согласно которой оператор goto имеет “гибельные последствия” и поэтому “должен быть исключен из всех языков программирования высокого уровня”. Исходя из этого были разработаны языки без goto: PDL, BLISS и др.
Вариант 2 отражает мнение ранних критиков Дейкстры, позиция которых выражается словами: “использование оператора goto может оказаться уместным в лучших структурированных программах”; “всегда были примеры программ, которые не содержат goto и аккуратно расположены лесенкой в соответствии с уровнем вложенности операторов, но совершенно непонятны, и были другие программы, содержащие goto и все же совершенно понятные”. Поэтому нужно “избегать использования goto всюду, где это возможно, но не ценой ясности программы”.
Как известно, полемика по goto “растревожила осиное гнездо” и всколыхнула “весь программистский мир”. Варианты 1 и 2 выражают крайние позиции участников дискуссии, между которыми, как казалось вначале, компромисс невозможен. Однако ситуация изменилась с изобретением и широким распространением заменителей goto, примерами которых являются: в языке СИ — операторы break, continue, return и функция еxit ( ), в языке МОДУЛА-2 — операторы RETURN, EXIT, процедура HALT и т. д.
Заменители — особый инструмент, который существенно отличается как от трех структурных управляющих конструкций, так и от goto. Они обладают двумя важными преимуществами по сравнению с goto:
Вариант 3 описывает языки СИ, АДА и др., где имеются заменители и на всякий случай сохраняется goto.
Вариант 4 соответствует языку МОДУЛА-2, где также есть заменители, однако goto исключен. Следует подчеркнуть, что при наличии заменителей сфера применения goto резко сужается, так что удельный вес ошибок, связанных с его применением, заметно уменьшается; поэтому вопрос об исключении goto теряет прежнюю остроту.
Идея структуризации оказала большое влияние на практику. Практически во все императивные языки высокого уровня на этапе их разработки или позже были введены структурные конструкции цикла и ветвления, а также, что очень важно, различные заменители goto.
Вместе с тем структурное программирование породило преувеличенные надежды, которые сменились разочарованием и упреками в невыполнении обещаний. В самом деле, на начальном этапе развития структурной технологии не было недостатка в оптимистических прогнозах. Структурный подход даже называли “революцией в программировании”. В 1972 г. Дейкстра писал: “Мы научились столь многому, что в течение ближайших лет программирование может превратиться в деятельность, во многом отличающуюся от того, что имеется сегодня, — настолько отличающуюся, что мы должны очень хорошо подготовить себя к ожидающему нас шоку... Семидесятые годы завершатся тем, что мы окажемся способны проектировать и реализовывать такие системы, которые в настоящее время требуют напряжения всех наших способностей, причем расходы на них будут составлять лишь небольшой процент в человекогодах от их сегодняшней стоимости, и, кроме того, эти системы будут фактически свободны от ошибок”.
Оправдались ли эти прогнозы? Вот что пишет Н. Брусенцов в 1985 г. (т. е. спустя пять лет после обозначенного Дейкстрой “контрольного срока”): “Ожидавшегося эффекта эти мероприятия пока не дали. Трудозатраты на разработку и сопровождение программ, если и уменьшились, то незначительно. Надежность по-прежнему остается острейшей проблемой. Даже рьяные поборники идеи структурирования признают, что революция не удалась”. В этих условиях некоторые авторы поспешили объявить структурное программирование “отживающим методом”.
Размышляя о причинах неудачи и стремясь поправить дело, И. Вельбицкий предлагает радикальным образом пересмотреть само понятие “структура программы”. По его мнению, “структура — понятие многомерное. Поэтому отображение этого понятия с помощью линейных текстов (последовательности операторов) сводит практически на нет преимущества структурного подхода. Огромные ассоциативные возможности зрительного аппарата и аппарата мышления человека используются практически вхолостую — для распознавания структурных образов в виде единообразной последовательности символов”.
Развивая мысль, Вельбицкий противопоставляет текстовое и визуальное программирование, приходит к заключению, что “на рельсах текстового представления программ” резервы повышения производительности труда в программировании исчерпаны, и делает вывод о необходимости изменить “базис” программирования, т. е. перейти от текстового программирования к визуальному.
В настоящее время визуальное программирование бурно развивается, число его сторонников растет. Тем не менее, уместно спросить: в какой мере предлагаемый Вельбицким пересмотр понятия “структура программы” согласуется с пионерскими взглядами Дейкстры?
Попытаемся еще раз заглянуть в темные переулки истории и внимательно перечитаем классический труд Дейкстры “Заметки по структурному программированию”. К немалому удивлению, мы обнаружим, что основной тезис о структурных управляющих конструкциях (для обозначения которых названный автор вводит термины “сочленение”, “выбор”, “повторение”) излагается с прямой апелляцией к визуальному языку блок-схем! Непосредственный анализ первоисточника со всей очевидностью подтверждает: дейкстрианская “структурная революция” началась с того, что Дейкстра, использовав блок-схемы как инструмент анализа структуры программ, предложил наряду с другими важными идеями четыре принципа структуризации блок-схем, которые в дальнейшем были преданы забвению или получили иное, по нашему мнению, слишком вольное толкование. Эти принципы таковы.
Хотя Дейкстра не дает словесной формулировки третьего и четвертого принципов, они однозначно вытекают из имеющихся в его работе иллюстраций. Чтобы у читателя не осталось сомнений, мы приводим точные копии подлинных рисунков Дейкстры (рис. 131, средняя и левая графа)1.
Таким образом, можно со всей определенностью утверждать, что две идеи (текстовое и визуальное структурное программирование), подобно близнецам, появились на божий свет одновременно. Однако этих близнецов ожидала разная судьба — судьба принца и нищего.
Далее события развивались довольно загадочным образом, поскольку вокруг видеоструктурной1 концепции Дейкстры образовался многолетний заговор молчания.
Неприятность в том, что изложенные выше рекомендации Дейкстры не были приняты во внимание разработчиками национальных и международных стандартов на блок-схемы (ГОСТ 19.701–90, стандарт ISO 5807–85 и т. д.). В итоге метод стандартизации, единственный метод, который мог бы улучшить массовую практику вычерчивания блок-схем, не был использован, а идея Дейкстры оказалась наглухо изолированной от реальных блок-схем, используемых в реальной практике программирования. В чем причина этой более чем странной ситуации?
Здесь уместно сделать отступление. Американский историк и философ Томас Кун в книге “Структура научных революций” говорит о том, что в истории науки время от времени появляются особые периоды, когда выдвигаются “несоизмеримые” с прежними взглядами научные идеи. Последние он, следуя Р. Бергману, называет парадигмами. История науки — история смены парадигм. Разные парадигмы — это разные образцы мышления ученых. Поэтому сторонникам старой парадигмы зачастую бывает сложно или даже невозможно понять сторонников новой парадигмы (новой системы идей), которая приходит на смену устоявшимся стереотипам научного мышления. По нашему мнению, текстовое и визуальное программирование — это две парадигмы, причем нынешний этап развития программирования есть болезненный процесс ломки прежних взглядов, в ходе которого прежняя текстовая парадигма постепенно уступает место новой визуальной парадигме. При этом — в соответствии с теорией Куна — многие, хотя и не все видные представители прежней, отживающей парадигмы проявляют своеобразную слепоту при восприятии новой парадигмы, которая в ходе неустанной борьбы идей в конечном итоге утверждает свое господство.
Этот небольшой экскурс в область истории и методологии науки позволяет лучше понять причины поразительного невнимания научного сообщества к изложенным Дейкстрой принципам структуризации блок-схем. Начнем по порядку. Формальная блок-схема — это двумерный чертеж, следовательно, она является инструментом визуального программирования. Отсюда следует, что предложенные Дейкстрой принципы структуризации блок-схем есть не что иное, как исторически первая попытка сформулировать основные (пусть далеко не полные и, возможно, нуждающиеся в улучшении) принципы видеоструктурного программирования.
Однако в 1972 г., в момент публикации работы Дейкстры, визуальное программирование как термин, понятие и концепция фактически еще не существовало. Поэтому — вполне естественно — суть концепции Дейкстры была воспринята сквозь призму господствовавших тогда догматов текстового программирования со всеми вытекающими последствиями. Так родилась приписываемая Дейкстре и по праву принадлежащая ему концепция текстового структурного программирования. При этом (что также вполне естественно) в означенное время никто не обратил внимания на тот чрезвычайно важный для нашего исследования факт, что исходная формулировка концепции Дейкстры имела явно выраженную визуальную компоненту — она представляла собой метод структуризации блок-схем, т. е. была описана в терминах видеоструктурного программирования.
Подобное невнимание привело к тому, что авторы стандартов на блок-схемы посчитали, что данная идея их не касается, ибо относится исключительно к тексту программ, и дружно проигнорировали или, скажем мягче, упустили из виду визуальную компоненту структурного метода Дейкстры.
Справедливости ради добавим, что и сам отец-основатель (Э. Дейкстра), обычно весьма настойчивый в продвижении и популяризации своих идей, отнесся к своему видеоструктурному детищу с удивительным безразличием и ни разу не выступил с предложением о закреплении структурной идеи в стандартах на блок-схемы.
Мы подошли к наиболее интригующему пункту в истории структурного программирования. Чтобы выявить главное звено проблемы, зададим вопрос: являются ли блок-схемы и структурное программирование взаимно исключающими, несовместимыми решениями? В литературе по этому вопросу наблюдается редкое единодушие: да, они несовместимы. Вот несколько отзывов. Блок-схемы “не согласуются со структурным программированием, поскольку в значительной степени ориентированы на использование goto”. Они “затемняют особенности программ, созданных по правилам структурного программирования”. “C появлением языков, отвечающих принципам структурного программирования,... блок-схемы стали отмирать”.
Парадокс в том, что приведенные высказывания основываются на недоразумении. Чтобы логический дефект стал очевидным, сопоставим две цитаты по методу “очной ставки” (табл. 5). Сравнивая мнение современных авторов с позицией Дейкстры, нетрудно убедиться, что описываемый критиками изъян действительно имеет место, но лишь в том случае, если правила вычерчивания блок-схем игнорируют предложенный Дейкстрой принцип ограничения топологии блок-схем. И наоборот, соблюдение указанного принципа сразу же ликвидирует недостаток.
Мнение критиков, убежденных в невозможности структуризации блок-схем | Предложение Э. Дейкстры о структуризации блок-схем |
“Основной недостаток блок-схем заключается в том, что они не приучают к аккуратности при разработке алгоритма: ромб можно поставить в любом месте блок-схемы, а от него повести выходы на какие угодно участки. Так можно быстро превратить программу в запутанный лабиринт, разобраться в котором через некоторое время не сможет даже сам ее автор” | Структуризация блок-схем с неизбежностью приводит “к ограничению топологии блок-схем по сравнению с различными блок-схемами, которые могут быть получены, если разрешить проведение стрелок из любого блока в любой другой блок. Отказавшись от большого разнообразия блок-схем и ограничившись... тремя операторами управления, мы следуем тем самым некоей последовательностной дисциплине” |
Проведенный анализ позволяет сделать несколько важных замечаний.
Наш интерес к структуризации и формализации блок-схем имеет и другую, не менее серьезную причину. Дело в том, что теоретическое программирование, в частности, теория схем программ (схематология) и теория оптимизирующих преобразований программ тесно связаны с теорией графов. По словам А. Ершова, “графы являются основной конструкцией для программиста”, так как они “обладают огромной, неисчерпаемой изобразительной силой, соразмерной масштабу задачи программирования”.
Для наших целей особенно важным является тот факт, что “графовая форма схем программ” или, что одно и то же, “графовая схема” (короче, граф-схема) аналогична блок-схеме. Более того, как убедительно показал А. Ершов, использовавший блок-схемы в качестве граф-схем, разграничение этих понятий является в некотором смысле условным. Различия в начертании (топологии) блок-схем и граф-схем несущественны, а наличие двух терминов оправдывается скорее разными сферами применения, так как термин “блок-схема” используется преимущественно в практическом, а “граф-схема” — в теоретическом программировании.
Изложенная выше постановка проблемы нуждается в обобщении и уточнении, что мы и сделаем в форме шести тезисов.
Перейдем теперь к анализу конкретных примеров, которые позволят в наглядной форме выявить различие блок-схем и дракон-схем.
С точки зрения правил языка ДРАКОН, первая блок-схема на рис. 132 (заимствованная из) имеет следующие недостатки.
Вторая блок-схема на рис. 132 имеет следующие изъяны.
Предыдущие два примера “плохих” блок-схем были случайным образом взяты из технической литературы. Следующий (третий) пример (см. рис. 132) скопирован из источника, где он характеризуется как “стандартная блок-схема ANSI” (Американский национальный институт стандартов). Блок-схема, выполненная по американскому стандарту, также имеет многочисленные дефекты:
Таким образом, американская блок-схема, как и предыдущие примеры, по всем параметрам проигрывает дракон-схеме.
Можно предложить девять правил, устанавливающих соответствие между понятиями шампур-метода и классического структурного кодирования.
Воспользуемся термином “базовые операции” для обозначения операций “ввод атома”, “добавление варианта” и “боковое присоединение”. Применяя к заготовке-примитиву базовые операции любое число раз, мы всегда получим структурную дракон-схему в традиционном смысле слова.
Шампур-метод позволяет строить как структурные, так и метаструктурные (лианные) программы. Выше мы выяснили, что базовые операции моделируют классическое структурное кодирование. Чтобы смоделировать полезные функции оператора goto, в шампур-методе предусмотрены операции “пересадка лианы” и “заземление лианы”.
Введем три понятия.
В примитиве могут использоваться только структурные и лианные блоки (рис. 133, 134), в силуэте — все три типа блоков: структурные, лианные и адресные (рис. 135, 136)1 .
Операции с лианой моделируют все без исключения функции заменителей goto (например, дополнительный выход из цикла), а также некоторые функции goto, которые невозможно реализовать с помощью заменителей. Однако они не приводят к хаосу, вызванному бесконтрольным использованием goto. С эргономической точки зрения, действия с лианой на порядок эффективнее и удобнее, чем goto и заменители; с другой стороны, они весьма эффективно корректируют недостатки классического структурного программирования.
Чтобы убедиться в этом на примере, вернемся к анализу рис. 27. В гл. 7 мы рассмотрели эргономические преимущества схемы на рис. 27б по сравнению с рис. 27а. Было показано, что улучшение эргономичности достигнуто за счет использования равносильных преобразований алгоритмов: вертикального и горизонтального объединения. При этом за кадром осталась важная проблема — проблема синтаксиса: как построить указанные схемы? Теперь мы имеем возможность осветить этот вопрос. Схема на рис. 27а представляет собой структурный блок, полученный с помощью операции “ввод атома”. В отличие от нее схема на рис. 27б — это лианный блок, построенный методом пересадки лианы.
Уместно вспомнить предостережение Г. Майерса: “Правила структурного программирования часто предписывают повторять одинаковые фрагменты программы в разных участках модуля, чтобы избавиться от употребления операторов goto. В этом случае лекарство хуже болезни; дублирование резко увеличивает возможность внесения ошибок при изменении модуля в будущем”. Как видно из рис. 26 и 27, пересадка лианы позволяет элегантно и без потерь решить эту непростую проблему, одновременно улучшая наглядность и понимаемость программы, обеспечивая более эффективное топологическое упорядочивание маршрутов.
Пересадка лианы узаконивает лишь некоторые, отнюдь не любые передачи управления, поскольку определение данной операции (см. гл. 15, тезис 28) содержит ряд ограничений. Запрет на образование нового цикла вызван тем, что переход на оператор, расположенный выше (раньше) в тексте программы, считается “наихудшим применением оператора goto”. Указанный запрет вводится, чтобы выполнить требование: использовать goto только для передачи управления вперед по программе, “которое некоторыми организациями принимается в качестве компромиссной версии структурного программирования”.
Уместно вспомнить предостережение Г. Майерса: “Правила структурного программирования часто предписывают повторять одинаковые фрагменты программы в разных участках модуля, чтобы избавиться от употребления операторов goto. В этом случае лекарство хуже болезни; дублирование резко увеличивает возможность внесения ошибок при изменении модуля в будущем”. Как видно из рис. 26 и 27, пересадка лианы позволяет элегантно и без потерь решить эту непростую проблему, одновременно улучшая наглядность и понимаемость программы, обеспечивая более эффективное топологическое упорядочивание маршрутов.
Ортодоксальный метод Дейкстры, полностью запрещающий goto и заменители (см. с. 238, табл. 4, вариант 1), безусловно является строгим формальным методом. К сожалению, он полезен лишь как интересная теоретическая идея, которая, как показал всемирный опыт, в чистом виде оказалась непригодной для массового использования.
По мнению специалистов, “правила структурного программирования верны в 95%. Но остаются злополучные 5%”. Чтобы поправить дело, решить проблему пяти процентов и создать метод, пригодный для широкой практики, пришлось пойти на компромисс и дать добро на использование заменителей и так называемое “ограниченное применение goto” (см. табл. 4, варианты 2—4). Благодаря этому проблема массовой практики программирования была решена. Но какой ценой? Ценой отказа от строгого формализма.
Это нетрудно показать. Например, авторы учебника языка СИ советуют осторожно и редко применять заменители break и continue, “поскольку слишком частое их использование ухудшает читаемость программы, увеличивает вероятность ошибок и затрудняет ее модификацию”. Далее они пишут: избегайте использовать goto, ибо это “чрезвычайно плохое” средство, которое следует применять “как можно реже или не применять совсем”.
Отсюда вытекает, что решение о списке случаев, когда нужно или не нужно употреблять перечисленные операторы, предоставляется программисту, который будет действовать, возможно, из лучших побуждений, но в соответствии со своим личным опытом и пристрастиями. Таким образом, о строгой формализации в данном случае речь идти не может.
Следовательно, мы вправе сделать существенное замечание. Структурное кодирование, используемое в широкой практике программирования, не является формальным методом, так как к формальным правилам Дейкстры пришлось добавить неформальные правила, касающиеся goto и заменителей.
В шампур-методе аналогом goto и заменителей служат формальные операции “пересадка лианы” и “заземление лианы”, на использование которых не накладывается никаких неформальных ограничений.
Тем самым мы приходим к важному выводу. В отличие от текстового структурного программирования, обладающего лишь частичной формализацией, правила визуального структурного программирования формализованы на 100%, что подтверждается тем фактом, что они реализованы в алгоритмах ДРАКОН-редактора.
Говоря о будущем структурного программирования, необходимо осознать, что текстовое и визуальное программирование опирается на разные системы понятий, которые по-разному расчленяют действительность. Поэтому видеоструктурное программирование нельзя рассматривать как результат механического перевода устоявшихся понятий классического структурного программирования на новый язык.
Поясним сказанное. При визуальном структурном программировании программист работает только с чертежом программы, не обращаясь к ее тексту, подобно тому, как программист, работающий, скажем, на Паскале, не обращается к ассемблеру и машинному коду — они для него просто не существуют! Это значит, что столь тщательно обоснованная Дейкстрой коллекция ключевых слов структурного кодирования (if, then, else, case, of, while, do, repeat, until, begin, end) при переходе к визуальному программированию становится ненужной — для программиста она просто перестает существовать! В равной степени становятся лишними и отмирают и другие ключевые слова, используемые оппонентами Дейкстры в различных вариантах расширения структурного кодирования: goto, continue, break, exit и т. д.
Следует специально подчеркнуть: поскольку в визуальном варианте структурного программирования ключевое слово goto не используется, теряют смысл и все споры относительно законности или незаконности, опасности или безопасности его применения, а также обширная литература, посвященная обсуждению этого, некогда казавшегося столь актуальным вопроса.
Предвижу возражения: хотя названные ключевые слова действительно исчезают, однако выражаемые ими понятия сохраняют силу и для визуального программирования. Например, две видеопрограммы на рис. 27 можно построить с помощью понятий if-then-else и goto. Данное возражение нельзя принять, поскольку произошла подмена предмета обсуждения. С помощью указанных понятий можно построить не видеопрограммы, а текстовые программы, эквивалентные видеопрограммам на рис. 27. Что касается собственно видеопрограмм, то они, будучи “выполнимой графикой”, строятся из “выполняемых” икон и “выполняемых” соединительных линий. Причем — подчеркнем еще раз — в процессе построения (которое реализуется с помощью ДРАКОН-редактора) пользователь не применяет понятия if-then-else и goto, ибо в этом нет никакой необходимости.
Нельзя путать задачу и систему понятий, на которую опирается метод ее решения. В обоих случаях — и при текстовом, и при визуальном структурном кодировании — ставится одна и та же задача: улучшить понимаемость программ и обеспечить более эффективный интеллектуальный контроль за передачами управления. Однако система понятий коренным образом меняется: ту функцию, которую в текстовой программе выполняют ключевые слова, в видеопрограмме реализуют совершенно другие понятия: иконы, макроиконы, соединительные линии, шампур, главная вертикаль шампур-блока, лиана, атом, пересадка лианы, запрет пересечения линий и т. д.
Очевидно, что использование понятий, выражаемых ключевыми словами классического структурного программирования, не является самоцелью, а служит лишь для того, чтобы “делать наши программы разумными, понятными и разумно управляемыми”. Указанные понятия решают эту задачу не во всех случаях, а только в рамках текстового программирования. При переходе к визуальному программированию задача решается по-другому, с помощью другого набора понятий. Именно отказ от старого набора понятий и замена его на новый и позволяет добиться новой постановки задачи и более эффективного ее решения. Поэтому высказываемое иногда критическое замечание: “недостаток шампур-метода в том, что он не удовлетворяет требованиям структурного программирования” является логически неправомерным. Нельзя упрекать самолет за то, что он не машет крыльями.