В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 14
Текст из файла (страница 14)
вам ЯП.
Подсказка. Проще всего это сделать по отношению к связыванию - в
традиционных ЯП этот аппарат развит слабо. Легко ли, например, на Паскале
определить ПОЯ, в котором возможно раздельно определять заголовки и тела
процедур, а связывать их по мере необходимости?
Итак, несмотря на свой весьма абстрактный характер (скорее, благодаря
ему), принцип цельности, обнаруживает "точки роста" ЯП, намечает тенденции
их развития, в частности, от языков (готовых) программ к языкам собственно
программирования, позволяющим с исчерпывающей полнотой управлять связыванием
компонент программы (ср. [3]).
3.5.1. Принцип цельности и нормальные алгоритмы
Принцип цельности носит неформальный, почти эстетический характер.
Способность оценивать уровень цельности языков и программ приходит только с
опытом. Чтобы лучше прочувствовать этот принцип, попробуем оценить на его
основе язык нормальных алгоритмов - модель Маркова. В этой модели всякая
программа представляет собой линейную последовательность однородных операций
(подстановок) над линейной последовательностью однородных данных (символов).
Связывание также однородно - просматривается последовательность операций и
последовательность данных, конкретная операция-подстановка связывается с
подходящей последовательностью данных. Затем эта операция выполняется и -
новый цикл связывания.
Отметим очевидную согласованность всех трех абстракций. Но эта
согласованность обслуживает не пошаговую детализацию, а простоту
исследования свойств нормальных алгоритмов (именно к этому и стремился их
изобретатель). Вместе с тем очевидно, что как основные абстракции, так и
средства развития в этой модели не удовлетворяют потребностям пошаговой
детализации.
Вопрос. В чем это проявляется?
Таким образом, качество ЯП не определяется критерием цельности самим по
себе. Влияние этого критерия на оценку качества ЯП зависит от того, какие
технологические потребности признаются определяющими.
[Известный тезис нормализации утверждает, что всякий алгоритм можно
заменить эквивалентным нормальным алгоритмом. Но важно хорошо понимать смысл
слова "можно" в этом тезисе. Можно заменить, если принять абстракцию
потенциальной осуществимости, отвлечься от таких "несущественных деталей",
как необходимые для этого ресурсы. Любой, кто писал нормальные алгоритмы,
прекрасно понимает, что ни одной реальной программы непосредственно в
исходной модели Маркова нельзя даже написать - она практически наверняка
будет неправильной и отладить ее будет невозможно в обозримое время (даже
если предположить сколь угодно высокую скорость выполнения самих марковских
подстановок). Ведь все выделяемые при программировании абстракции, как
данных, так операций и связывания, нужно подразумевать или хранить вне
программы. Поэтому единственный разумный путь к практической осуществимости
программирования на языке нормальных алгоритмов - моделировать на этом языке
другой, более совершенный в технологическом отношении язык].
3.5.2. Принцип цельности и Ада. Критерий цельности
Как видно на примере Ады, в более современных ЯП принцип согласования
абстракций (как между собой, так и с важнейшими технологическими
потребностями) осознан и учтен в гораздо большей степени. Взглянем на шаги с
3.1 по 3.8 с точки зрения потребности согласовывать абстракции.
Выделив на шаге 3.1 операционную абстракцию - функцию все_связи, мы
немедленно ощутили потребность обозначить классы возможных аргументов и
результатов этой функции, не занимаясь их детальной проработкой. Если бы
приходилось работать на Фортране, Алголе-60 или Бейсике, сделать это
оказалось бы невозможным - непосредственно подходящих предопределенных типов
данных в этих ЯП нет, а возможность строить новые типы также отсутствует.
Скорее всего пришлось бы нарушить естественный порядок детализации и сначала
придумать способ представлять "связи" некоторым массивом, а затем учесть,
что в этих ЯП функции не вырабатывают результаты-массивы (только скаляры) и
представить нужную абстракцию не функцией, а процедурой. Важно понимать, что
такого рода отклонения запутывают логику программы, провоцируют ошибки,
затрудняют отладку и т.п.
Лучшее, что можно сделать в этом случае - выйти за рамки используемого
ЯП и фиксировать шаги детализации на частично-формализованном псевдокоде. О
применении такого псевдокода в структурном подходе к программированию на
классических ЯП можно прочитать, например, в [6].
В Аде мы смогли провести шаг детализации полностью в рамках языка.
Причем, вводя операционную абстракцию, были вынуждены воспользоваться
средствами определения абстракций другого рода - абстракций данных. Точнее
говоря, на шаге 3.1 мы воспользовались лишь тем, что в Аде можно вводить
новые абстракции данных и можно вводить для этих абстракций подходящие
названия.
Обратите внимание, мы ввели не названия отдельных объектов данных
(только так и можно в классических ЯП), а именно названия целых классов
(типов) обрабатываемых объектов.
Определения типов мы также имели возможность вводить по шагам, вполне
аналогично тому, как в классических ЯП вводят операционные абстракции,
выделяя нужные процедуры. На шаге 3.1 обозначили новый тип "связи"; на шаге
3.5 уточнили его строение, но потребовались названия типов число_связей и
перечень_связей. На шагах 3.6 и 3.8 уточнили строение этих типов, но
остались неопределенными макс_узлов и макс_связей. Наконец, уточнили их
характеристики и даже значения.
Четкость пошаговой детализации поддерживалась языковыми средствами
связывания. Можно было не заботиться о реализации процедур и функций - ее
можно определить позже, в теле пакета - средства связывания обеспечат
согласованное использование спецификаций и тел процедур. В классических ЯП
так поступить нельзя, пришлось бы опять выходить за рамки языка или нарушать
порядок детализации и выписывать тела процедур (а это не всегда можно
сделать, еще не зная структуру используемых данных).
Итак, должно быть видно, как принцип согласования основных абстракций
(между собой и с потребностями пошаговой детализации) воплощен в Аде. Во-
первых, согласованность основных абстракций действительно требовалась, и,
во-вторых, Ада необходимые выразительные средства предоставляет.
Принцип цельности дает основания ввести критерий технической оценки
языка, который можно назвать критерием цельности: язык тем лучше, чем ближе
он к идеалу с точки зрения принципа согласования абстракций. Ясно, что с
точки зрения технологии пошаговой детализации Ада превосходит классические
ЯП по этому критерию.
Упражнение. Пользуясь критерием цельности, оцените другие известные
ЯП.
4. Данные и типы
4.1. Классификация данных
Рассматривая три выделенные роли в акте исполнителя, мы подчеркивали,
что с ролью данных ассоциируется пассивное начало. Это не означает и не
требует полной пассивности объекта, выступающего в роли данного, а лишь его
относительной пассивности с точки зрения рассматриваемого акта поведения
того исполнителя, планирование поведения которого нас интересует. Тот же
объект с другой точки зрения может быть активным и даже сам выступать в роли
исполнителя. В качестве примера можно привести задачу управления
асинхронными процессами, когда осуществляющий управление исполнитель вправе
рассматривать эти (активные) процессы как данные, на которые направлено его
управляющее воздействие.
Данными обычно считают любые обрабатываемые объекты, независимо от их
внутренней природы. Одна и та же категория объектов в одном ЯП может
выступать в роли данных, а в другом это может быть запрещено. Так, процедуры
могут быть данными в Паскале и Алголе-68 (их можно передавать в качестве
значений, присваивать компонентам других объектов), но не в Аде.
Данные различаются по многим признакам.
Во-первых, данные можно классифицировать по содержательным ролям,
которые они играют в решаемой эадаче. Очень заманчиво было бы уметь явно
отражать в программе результаты такой классификации с тем, чтобы сделать ее
доступной как исполнителю, так и читателю программы. По существу это
прогнозирование поведения определенных объектов данных (например, прогноз о
том, что переменные A и B никогда не могут быть операндами одного и того же
сложения). Прогноз такого рода облегчает понимание программы и создает
предпосылки для автоматического содержательного контроля.
Возможность отражать содержательную классификацию данных отсутствует в
большинстве классических ЯП. В Аде сделаны шаги в нужном направлении.
Например, мы различали типы "узел", индекс-узла и число-связей, хотя все они
в конечном итоге представлены целыми числами.
Во-вторых, данные различаются по своему внутреннему строению,
структуре, характеру связей своих составляющих. Например, массивы, таблицы,
списки, очереди. С этой точки зрения важен способ доступа к составляющим
данных. Классификация данных по способу доступа к составляющим обычно
имеется в виду, когда говорят о структурах данных. Классификация данных по
их структуре есть в том или ином варианте почти во всех ЯП. В современных ЯП
чаще всего выделяются массивы и записи. И то, и другое можно считать частным
случаем таблиц, которые служат ключевой структурой, например, в МАСОНе [10]
и его последующих модификациях.
В-третьих, данные различаются по своей изменчивости. Например, в
некоторых случаях известен диапазон возможных изменений или известно, что
данное вообще не должно меняться. Прогнозирование поведения такого рода
встречается только в относительно новых ЯП, начиная с Паскаля.
В-четвертых, данные могут различаться по способу своего определения.
Их свойства могут быть предопределены (т.е. определены автором ЯП) или же
определены программистом с помощью языковых средств. В последнем случае в
идеале это такие средства, которые позволяют программисту по существу
определить новый язык, обогащая исходный.
[Как правило, предопределенные объекты и свойства не могут быть
изменены программистом и в этом смысле надежно защищены от искажений. У
программиста должна быть возможность принять меры к тому, чтобы вновь
введенные им абстракции были неотличимы от предопределенных. Это еще одна
формулировка принципа защиты абстракций.
Если защита обеспечена, то на каждом шаге обогащения ЯП появляется
полная возможность действовать так, как будто в распоряжении программиста
появился виртуальный исполнитель для сконструированного уровня абстракции.
Подчеркнем, что при этом детализация осуществляется от задачи к реализации
(сверху - вниз), а создание виртуальных машин - в общем случае от реальной
машины к задаче (снизу-вверх). Аппарат для определения данных,
ориентированный на принцип защиты абстракций, имеется только в новейших ЯП,
в частности, в Аде, Модуле-2, последних версиях Паскаля и др.]
В-пятых, данные могут различаться по своему представлению на более
низком уровне абстракции (на реализующей виртуальной машине, в терминах
реализующей структуры данных, по классу необходимых для реализации ресурсов,
по объему и дисциплине использования памяти и т.п.). Например, для чисел
может требоваться одно слово, два или несколько в зависимости от нужной
точности вычислений, память для данных одной категории может выделяться в
некотором стеке (например, для локальных данных блоков) или в так называемой
куче (для элементов динамически изменяемых списковых структур), для
некоторых данных разумно выделять самую быструю память (например, быстрые
регистры для переменной цикла). Это еще одна форма прогнозирования поведения
объектов - запрос для них подходящих ресурсов. Классификация с точки зрения
представления встречается практически во всех ЯП, ориентированных на
эффективное использование ресурсов машины, в частности, в языке Си.
В-шестых, данные могут различаться по применимым операциям (внешним
свойствам), определяющим возможности данного играть определенные роли или
вступать в определенные отношения с другими объектами программы.
Например, если данное представляет собой число, символ, указатель,
задачу, очередь, стек, то в каждом из этих случаев к нему применим
определенный набор операций, у него имеется определенный набор атрибутов,
характерных для данных именно этого класса и т.п. Чтобы не было путаницы с
первым, фактором классификации, подчеркнем, что переменная для хранения
числа апельсинов может отличаться от переменной для хранения числа яблок по
содержательной роли, но не отличаться по применимым операциям. А вот объекты
типов "узел" и индекс_узла - различаются по применимым операциям (по
каким?).
Наконец, в-седьмых, данные могут различаться по характеру доступа к
ним. Одни данные считаются общедоступными, другие могут использоваться
только определенными модулями или при определенных условиях и т.п.