Правильная ссылка на эту страницу
http://az-design.ru/Support/SoftWare/Delphi/Pascal/001b3010.shtml

10. ПОЧЕМУ ЯЗЫК ПРОГРАММИРОВАНИЯ ПАСКАЛЬ НЕ ЯВЛЯЕТСЯ МОИМ САМЫМ ЛЮБИМЫМ ЯЗЫКОМ ПРОГРАММИРОВАНИЯ

Б.Керниган1{Фирма Bell Laboratories, Марри-Хилл, США.}

10.1. ИСТОРИЯ ПОЯВЛЕНИЯ НАСТОЯЩЕЙ СТАТЬИ

       Толчком к написанию настоящей статьи послужили два события. Первое такое событие — появление ряда работ, посвященных сравнению языков Си и Паскаль [48, 119, 144]. Вторым таким событием была попытка переработать книгу [99], используя в ней вместо языка Ратфор язык Паскаль.
       Сравнивать языки Си и Паскаль — все равно что сравнивать самолеты "Лир Джет" и "Пайпер Каб"2{Здесь необходимы некоторые пояснения. "Лир Джет" — небольшой двухмоторный реактивный самолет (примерно как ЯК-40) с повышенной комфортабельностью, используемый высокопоставленными лицами (уровня президентов и первых вице-президентов крупных и преуспевающих фирм). В переносном смысле "Лир Джет" — символ благополучия и преуспевания. Поэтому для любого американца сказанная кем-либо фраза о том, что фирма, сотрудником которой является его собеседник, имеет собственный "Лир Джет", говорит очень о многом. "Пайпер Каб" — маленький одномоторный поршневой самолетик с верхним расположением крыла (примерно как ЯК-12),используемый для обучения летчиков. Для любого американца "Пайпер Каб" ассоциируется с азбукой, поскольку это первый самолет для тех, кто решил научиться управлять самолетом (его можно уподобить легендарному, прозванному "летающей партой" отечественному самолету У-2). — Прим. ред.}, поскольку если язык Си предназначен для создания программного обеспечения, то язык Паскаль — для обучения программированию, а поэтому такое сравнение будет просто неверным. Упоминавшаяся выше попытка переработки книги [99] дает повод для проведения более естественного сравнения. Изначально примеры программ в книге [99] были написаны на языке Ратфор, представляющем собой "структурированный" диалект языка Фортран, реализованный в виде препроцессора.
       Поскольку язык Ратфор — всего лишь замаскированный язык Фортран, то в нем отсутствует ряд ценных качеств, появившихся в языке Паскаль, в котором средства типизации данных лучше подходят для обработки символов, а сильная типизация вместе с имеющимися средствами структуризации данных облегчает работу программиста.
       Однако переписать программы с языка Ратфор на язык Паскаль неожиданно оказалось намного сложнее, чем предполагалось. Настоящая статья представляет собой попытку извлечь из полученного опыта некоторые уроки того, насколько язык Паскаль подходит для реального программирования , (в отличие от обучения программированию). Таким образом, настоящая статья не является сравнением языка Паскаль ни с языком Си, ни с языком Ратфор.
       Первоначально вышеупомянутые программы были переписаны на один из диалектов языка Паскаль, являющийся входным языком для интерпретатора языка Паскаль pi, реализованного в Калифорнийском университете (г. Беркли). Этот диалект языка Паскаль весьма близок к номинальному стандарту на язык Паскаль [91], а интерпретатор языка Паскаль pi обеспечивает хорошую диагностику и тщательное проведение всех необходимых проверок на этапе выполнения. Поэтому оказалось возможным использовать программы, написанные на этом диалекте языка Паскаль, без изменения (за исключением библиотечных функций и процедур), применяя другие трансляторы с языка Паскаль. В частности, применялись следующие четыре транслятора с языка Паскаль: интерпретатор языка Паскаль VU, разработанный в Амстердамском университете, транслятор с языка Паскаль для мини-ЭВМ семейства VAX-11, разработанный в Калифорнийском университете (г. Беркли), транслятор с языка Паскаль, разработанный фирмой Whitesmiths, и транслятор с языка UCSD Паскаль для микроЭВМ, построенных на основе микропроцессора Z80. Из этих четырех трансляторов с языка Паскаль первые три реализованы на языке Си.
       Язык Паскаль является широко обсуждаемым языком программирования. Опубликованная недавно2{Необходимо помнить, что статья написана в 1981 г. - Прим. ред.} тематическая библиография по языку Паскаль [122], подготовленная Д.Моффатом, содержит в разделе "Обсуждение, анализ, дебаты" перечень 175 работ. Наиболее часто цитируемыми работами в этой области (а значит, работами, которые стоит читать) являются резкая критика языка Паскаль А.Хаберманом [64] и столь же резкий ответ на нее О.Лекарма и П.Дежардена [111]3{Обе эти статьи опубликованы в настоящем сборнике. - Прим. ред.}. Кроме того, можно рекомендовать для чтения статью Н.Вирта [174] и статью X.Бума и Э. Де Джонга [21]4{Эта статья также опубликована в настоящем сборнике. - Прим. ред.}. У автора нет ни желания, ни возможности подводить итоги широкого обсуждения языка Паскаль (оно уже нашло свое отражение в большом количестве работ на эту тему). В настоящей статье автор представляет свои собственные наблюдения, которые, конечно же, иногда будут дублировать результаты других специалистов. Дальнейшее изложение построено по следующей схеме:
       1. Типы и области действия.
       2. Управление последовательностью действий.
       3. Окружение.
       4. Косметика.
       Внутри каждого раздела вопросы рассматриваются в порядке убывания их значимости.
       Автор может резюмировать свою точку зрения на язык Паскаль следующим образом: язык Паскаль, возможно, и является замечательным средством для обучения программированию (у автора, однако, соответствующий опыт отсутствует); язык Паскаль представляет собой значительное достижение, имея в виду, что он был создан в 1969г.; язык Паскаль, безусловно, оказал огромное влияние на разработку ряда языков программирования, из которых наибольшее значение имеет язык Ада; но в своем современном варианте язык Паскаль не является адекватным средством для создания реальных программ; язык Паскаль пригоден лишь для создания небольших независимых программ, в которых осуществляется лишь тривиальное и ограниченное взаимодействие с окружением и не используется программное обеспечение, написанное кем-либо другим.

10.2. ТИПЫ И ОБЛАСТИ ДЕЙСТВИЯ

       Язык Паскаль почти является языком программирования с сильной типизацией. Грубо говоря, это означает, что в программе, написанной на языке Паскаль, каждый объект имеет тип, определяющий допустимые значения этого объекта и набор операций, применимых к этому объекту. При этом транслятор с языка Паскаль гарантирует невозможность присваивания объектам недопустимых значений и применения к объектам недопустимых операций за счет некоторой совокупности проверок, осуществляемых как на этапе трансляции, так и на этапе выполнения. Однако, конечно же, реальные трансляторы с языка Паскаль могут не обеспечивать проведения всех без исключения проверок, являющихся обязательными с точки зрения языка Паскаль. Более того, не следует путать сильную типизацию с анализом размерности. Например, если определить типы apple и orange следующим образом:

type
    apple = integer;
    orange = integer;

       то любое арифметическое выражение, в которое входят объекты типа apple, типа orange, будет абсолютно корректным.
       Сильная типизация имеет различные проявления. Так, параметры, передаваемые в процедуры и функции, подвергаются контролю на соответствие типов. Это означает, что с той свободой, благодаря которой в языке Фортран ничего не стоило передать в качестве параметра значение в форме с плавающей точкой в подпрограмму, параметром которой должно быть значение в форме с фиксированной точкой, наконец покончено. Автор полагает, что это очень полезная особенность языка Паскаль, предотвращающая ошибки.
       Переменные целого типа, могут быть описаны так, что будет указан диапазон их допустимых значений для того, чтобы транслятор с языка Паскаль и исполнительная система не допускали присваивания этим переменным недопустимых (например, слишком больших) значений. Это тоже представляется весьма удобным, хотя естественно, что за такой сервис приходится расплачиваться ростом накладных расходов на этапе выполнения.
       Перейдем, однако, к рассмотрению вопросов, связанных с типами и областями действий.

10.2.1. РАЗМЕР МАССИВА ЯВЛЯЕТСЯ ЧАСТЬЮ ЕГО ТИПА

       Если в написанной на языке Паскаль программе имеются следующие описания:

var arr10 : array [1..10] of integer;
    arr20 : array [1..20] of integer;

       то arr10 и arr20 будут массивами, состоящими из 10 и 20 целых элементов соответственно. Предположим, что требуется написать процедуру sort, обеспечивающую возможность сортировки массивов, состоящих из целых значений (элементов). Поскольку массивы arr10 и arr20 имеют различные типы, то оказывается невозможным написать универсальную процедуру, пригодную для сортировки любого из них1{По этому поводу см. также §1.3.1 статьи А.Фьюэра и Н.Джехани "Сравнение языков программирования Си и Паскаль*', §2.3.2 статьи П.Матети "Паскаль против Си: субъективное сравнение двух языков программирования"' и §9.4.1 статьи О.Лекарма и П.Дежардена "Дополнительные замечания по поводу языка программирования Паскаль", помещенных в настоящем сборнике. - Прим. ред.}.
       По мнению автора, это причина одной из трудностей, встретившихся в процессе переработки книги [99]. Кроме того, по мнению автора, это оказывает влияние на разработку программного обеспечения вообще, поскольку затрудняет создание достаточно универсальных библиотечных подпрограмм общего назначения, таких, например, как подпрограммы сортировки.
       Особенно ощутим этот недостаток при работе с массивами символов, поскольку в языке Паскаль строки символов представляются в виде массивов символов. Предположим, что требуется написать функцию index (s, с), которая в качестве своего значейия возвращает номер позиции в строке символов s, при первом вхождении символа с в эту строку и нуль в случае отсутствия вхождения. Проблема заключается в том, как обращаться с аргументом функции index, специфицирующим строку s. Оба следующих обращения к функции index: index ('hello', с) и index ('goodbye', с) не могут быть правильными, поскольку строки символов 'hello' и 'goodbye' состоят из различного числа символов (при этом автор обходит молчанием то, каким образом можно определить конец строки символов типа 'hello', ведь такого способа не существует).
       Можно попытаться пойти другим путем:

var temp : array [1..10] of char;
temp := 'hello';
n := index (temp, c);

       Но такое решение тоже не годится: использование оператора присваивания temp : = 'hello' недопустимо вследствие того, что длина строки символов 'hello' не совпадает с числом символов в массиве temp.
       Единственным выходом из сложившейся ситуации является либо создание семейства подпрограмм, в составе которого имеется столько подпрограмм, сколько может быть строк символов различной длины, либо выравнивание длин всех строк символов до одинаковой длины.
       Последнее — меньшее из двух зол. Поэтому в книге [100] тип string описан следующим образом:

type string = array [1..MAXSTR] of char;

       где константа MAXSTR "достаточно велика", чтобы длина строки символов в любой из встречающихся (в книге [100]) программ не превышала ее. Такой подход, конечно же, далек от идеального, хотя и позволяет создать вполне работоспособные программы. Однако такой подход не решает проблемы создания библиотечных подпрограмм, пригодных для использования в режиме промышленной эксплуатации.
       Существует ряд ситуаций, в которых использование представления строк символов в виде массивов символов фиксированной (раз и навсегда заданной) длины просто неприемлемо. Например, в книге [100] программа сортировки строк символов в процессе выполнения пытается разместить в памяти столько строк символов, сколько в ней уместится, т.е. время ее выполнения существенно зависит от плотности упаковки памяти, а именно от того, сколько строк одновременно уместится в памяти. Поэтому для строк символов в этом случае необходимо выбрать другое представление, отличное от массивов фиксированной (раз и навсегда заданной) длины. Для этого можно воспользоваться механизмом, аналогичным механизму указателей, — упаковать все строки символов в один большой массив символов и ввести еще один массив целых значений, элементы которого содержат индексы начала каждой из строк символов в первом массиве символов:

type charbuf *nbsp; = array [1..MAXBUF] of char;
     charindex = array [l..MAXINDEX] of 0..MAXBUF;

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

packed array [1..n] of char

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

error ('короткое сообщение об ошибке                   ');
error ('это несколько более длинное сообщение об ошибке');

       Ряд реализаций трансляторов с языка Паскаль допускает возможность использования типа данных "строка символов", что позволяет избежать этой проблемы. При этом строки символов считаются имеющими один и тот же тип независимо от их длины. Это, конечно, решает проблему для конкретного типа данных (строк символов), но не решает проблемы для других типов данных. Кроме того, это не обеспечивает возможности решения одной из вторичных проблем — вычисления длины константной строки символов, для решения которой обычно используется специальная встраиваемая функция.
       Энтузиасты языка Паскаль часто заявляют, что проблемы, связанные с размерами массивов, могут быть решены раз и навсегда путем взятия копии библиотечной подпрограммы и подстановки в ее описание соответствующих конкретному случаю параметров. Однако следующее заявление звучит в лучшем случае слабо [111]:

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

       Этот недостаток является причиной возникновения важнейшей из проблем, связанных с использованием языка Паскаль. Автор уверен, что устранение этого недостатка на порядок повысило бы качество языка Паскаль и существенно расширило бы его использование. В стандарте ISO на язык Паскаль [2] предложен способ устранения этого недостатка

10.2.2. ОТСУТСТВИЕ СТАТИЧЕСКИХ ПЕРЕМЕННЫХ И ВОЗМОЖНОСТИ ИНИЦИАЛИЗАЦИИ ПЕРЕМЕННЫХ НА ЭТАПЕ ТРАНСЛЯЦИИ

       Статические переменные (для описания которых в языке Алгол 60 используется описатель own) недоступны вне подпрограмм, внутри которых описаны переменные, сохраняющие свои значения в промежутках между обращениями к этим подпрограммам. В языке Фортран переменные de facto являются статическими, кроме тех, что фигурируют в общих (COMMON) блоках1{Строго говоря, при использовании языка Фортран 77 необходимо использовать оператор SAVE.}, а в языке Си для описания локальных переменных может быть использован описатель static.
       В языке Паскаль такой класс памяти отсутствует. Это означает, что если при использовании языка Паскаль необходимо, чтобы некоторая переменная сохраняла свое значение в промежутках между обращениями к некоторой функции или процедуре, то эта переменная должна быть внешней по отношению к соответствующей функции или процедуре. То есть эта переменная оказывается доступной для других функций и процедур, а ее имя должно быть уникальным в значительно более обширной области действия. Простым примером, иллюстрирующим проблемы, возникающие в результате отсутствия статических переменных, является подпрограмма для генерирования случайных чисел: сгенерированное при i-м обращении к такой подпрограмме случайное число должно быть сохранено, поскольку при (i+ 1)-м обращении к этой подпрограмме оно используется в качестве начального значения. Для этого сгенерированное случайное число должно размещаться в переменной, время жизйи которой охватывает все обращения к подпрограмме генерирования случайных чисел. На практике это означает, как правило, необходимость описания этой переменной в самом внешнем блоке программы. А это приводит к тому, что описание этой переменной оказывается расположенным далеко от того места в программе, где эта переменная используется.
       Рассмотрим пример из гл.7 книги [100], связанный с форматированием текстов. Переменная dir определяет, с какой стороны слова будет вставлен пробел в процессе выравнивания строк текста по правому краю. Программа на языке Паскаль выглядит следующим образом:

program formatter (...); 

var
   dir: 0..1; {определяет, с какой стороны добавлять пробелы}
   . . .
   . . .
   . . .

procedure justify (...);

begin
   dir := 1 - dir; {с противоположной, чем в предыдущий раз}
   . . .

end;
   . . .

begin {основная программа}
   dir := 0;
   . . .

end;

       Легко заметить, что описание, инициализация и использование переменной dir осуществляются в совершенно различных местах программы — в начале, конце и середине. По этой причине описание некоторой переменной может отстоять на сотни строк от места ее инициализации и еще на сотни строк от того места, где эта переменная используется. При использовании языков Си или Фортран переменная dir может быть сделана локальной в использующей ее подпрограмме. Следующий пример иллюстрирует, как можно решить эту задачу при использовании языка программирования Си:

   . . .
main ()
{
   . . .
}
   . . .
justify ()
{
   static int dir = 0;
   dir = 1 - dir;
   . . .
}

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

int dir = 0;

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

dir := 0;

       Это, удлиняет и исходный текст программы, и объектный код, генерируемый транслятором с языка Паскаль.
       Более того, отсутствие в языке Паскаль средств инициализации переменных произвольными значениями на этапе трансляции обостряет проблему искусственного увеличения областей действия переменных, вызванную отсутствием в языке Паскаль статических переменных. Поскольку инициализация переменных обычно производится в начале программы, то основная программа (написанная на языке Паскаль) должна начинаться либо с многочисленных инициализаций переменных, либо с обращений к подпрограммам, обеспечивающим выполнение соответствующих инициализаций переменных. В любом из этих двух случаев переменные, подлежащие инициализации, должны иметь область действия, позволяющую произвести их инициализацию путем явного присваивания им соответствующих значений, а это означает, что эти переменные должны быть описаны в самом внешнем блоке программы. В результате любая подлежащая инициализации переменная является глобальной.
       Еще одна трудность — отсутствие какого-либо другого способа сделать некоторую переменную разделяемой между двумя подпрограммами, кроме как описать эту переменную на более высоком, чем находятся обе эти подпрограммы, уровне. Однако в этом случае эта переменная окажется доступной для всех находящихся на этом уровне подпрограмм, не говоря уж о том месте, где она описана. В языке Фортран имеется механизм общих (COMMON) блоков, а в языке Си - класс памяти external, позволяющие избежать этого и сделать возможным разделение некоторой переменной только между двумя (или несколькими подпрограммами).
       В стандарте ISO на язык Паскаль [2] не введены ни статические переменные, ни средства инициализации переменных произвольными значениями на этапе трансляции.

10.2.3. СВЯЗАННЫЕ ДРУГ С ДРУГОМ КОМПОНЕНТЫ ПРОГРАММЫ ДОЛЖНЫ НАХОДИТЬСЯ В РАЗЛИЧНЫХ МЕСТАХ ПРОГРАММЫ

       Поскольку язык Паскаль разработан так, чтобы можно было реализовать транслятор с него по однопроходной схеме, то необходимо, чтобы описания любых объектов предшествовали их использованию. В частности, это касается процедур и функций. В результате типичная программа, написанная на языке Паскаль, имеет странную структуру — основная программа расположена в конце, а ей предшествуют используемые в основной программе процедуры и функции. Такая структура программы практически прямо противоположна процессу проектирования и реализации программ (основной программы, процедур и функций).
       До некоторой степени это можно смягчить, используя механизм, реализованный в языках Си и Ратфор с помощью конструкции #include, обрабатываемой препроцессором транслятора с языка Си: файлы, содержащие исходный текст различных компонентов программы, могут быть включены в нужное место программы, не внося беспорядка в программу и не загромождая ее. Конструкция #include не является частью языка Паскаль, но реализована в первых трех из четырех упомянутых в разд. 10.1 настоящей статьи трансляторах с языка Паскаль.
       В языке Паскаль имеется также описание forward, позволяющее отделить описания заголовков процедур и функций от описания их тел. Основной целью введения в язык Паскаль описания forward является обеспечение возможности описания взаимно рекурсивных процедур.
       С этим связана также проблема строгой последовательности описания различных классов объектов, предписываемой языком Паскаль. Каждая процедура или функция имеет раз и навсегда установленную структуру:

label Описание меток, если таковые имеются
const Описания констант, если такове имеются
type Описания типов, если таковые имеются
var Описания переменных, если таковые имеются
procedure или function Описания процедур и функций, если таковые имеются
begin  
  Тело функции или процедуры
end  

       Это означает, что все описания объектов каждого из имеющихся классов должны быть для удобства транслятора с языка Паскаль сгруппированы по классам независимо от логической связи описываемых объектов друг с другом, что может привести к ухудшению "удобочитаемости" программ. Поскольку при использовании языка Паскаль вся написанная на нем программа (т.е. все ее компоненты без исключения) транслируется один раз, то чрезвычайно редко удается удержать поблизости друг от друга описания переменных, их инициализацию и использование. Даже самые ярые сторонники языка Паскаль соглашаются с этим [167]:

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

       Возможность включения файлов, предоставляемая конструкцией #include, позволяет лишь чуть-чуть улучшить ситуацию, но не позволяет изменить ее кардинальным образом.
       Стандарт ISO на язык Паскаль [2] не принес с собой никаких изменений, связанных с ослаблением строгости последовательности описания различных классов объектов.

10.2.4. ОТСУТСТВИЕ ВОЗМОЖНОСТИ ПРОВЕДЕНИЯ РАЗДЕЛЬНОЙ ТРАНСЛЯЦИИ

       Язык Паскаль не обеспечивает возможности проведения раздельной трансляции. Поэтому авторы каждой реализации транслятора с языка Паскаль принимают на свой страх и риск решение о включении (или не включении) в осуществляемую ими реализацию возможности проведения раздельной трансляции. Например, многие трансляторы с языка Паскаль не допускают возможности проведения раздельной трансляции (как, например, интерпретатор языка Паскаль для мини-ЭВМ семейства VAX-11, разработанный в Калифорнийском университете (г.Беркли), упоминавшийся в разд.10.1), что соответствует и духу и букве языка Паскаль. Ряд трансляторов с языка Паскаль обеспечивает возможность описания того, что тело некоторой подпрограммы описано где-то в другом месте. Однако, как бы то ни было, все механизмы такого рода являются нестандартными и отличаются от реализации к реализации.
       Теоретически необходимость проведения раздельной трансляции отсутствует: при наличии очень быстро работающего транслятора с некоторого языка (если исходный текст для всех компонентов программы постоянно доступен и имеется возможность включения файлов, позволяющая избежать дублирования исходного текста) может быть использована перетрансляция всей программы. На практике же, конечно, ни один транслятор с любого языка программирования не достигает требуемого для этого быстродействия, исходный текст различных компонентов программы часто оказывается недоступным, а возможность включения файлов обычно не обеспечивается средствами используемого языка. Все это делает внесение изменений в достаточно большую программу весьма трудоемкой задачей.
       Ряд трансляторов с языка Паскаль допускает возможность проведения раздельной трансляции, не обеспечивая проведение полного контроля соответствия типов в раздельно транслируемых компонентах программы. Это образует огромную "дыру" в механизме сильной типизации (надо, однако, признать, что язык Паскаль не самый худший в этом отношении, поскольку в других языках программирования вообще не обеспечивается проведение никакого контроля соответствия типов в раздельно транслируемых компонентах программы). Как курьез, связанный с этой темой, автору вспомнилась одна статья (к счастью, ненапечатанная), в которой на странице n язык Си критиковался за отсутствие контроля соответствия типов в раздельно транслируемых компонентах программы, а на странице n+1 предлагался способ нарушения механизма сильной типизации в языке Паскаль путем введения возможности проведения раздельной трансляции без проведения полного контроля соответствия типов в раздельно транслируемых компонентах программы.
       Стандарт ISO на язык Паскаль [2] не включает возможности проведения раздельной трансляции.

10.2.5. ЕЩЕ НЕКОТОРЫЕ ПРОБЛЕМЫ, СВЯЗАННЫЕ С ТИПАМИ И ОБЛАСТЯМИ ДЕЙСТВИЯ

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

procedure add10 (var a: array [1..10] of integer);

       Корректно выполнить необходимые для этого действия нужно так:

type al0 = array [1..10] of integer;
. . .

procedure add10 (var a: a10);

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

if (с = blank) or (с = tab) or (с = newline) then ... 

       может быть записана более коротко и понятно следующим образом:

if с in [blank, tab, newline] then ...

       Однако на практике тип множества не получил столь широкого, как мог бы, распространения (его использование, как правило, ограничено случаями, аналогичными приведенному выше примеру), поскольку максимально возможный размер множества не определен в языке Паскаль и существенно зависит от реализации. Возможно, что причиной этого является то, что самая первая реализация транслятора с языка Паскаль, осуществленная для ЭВМ семейства CDC 6000, обеспечивала возможность использования достаточно больших множеств (59 бит). Например, представляется естественным написать функцию isalphanum, обеспечивающую проверку, является ли. передаваемый ей в качестве параметра символ буквой или цифрой, следующим образом:

{isalphanum (с) — истина, если с является буквой или цифрой}

function isalphanum (с: char): boolean;

begin
   isalphanum := с in ['a'..'z','A'..'Z','0'..'9']

end;

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

10.2.6. ВЫХОДА НЕТ

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

10.3. УПРАВЛЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТЬЮ ДЕЙСТВИЙ

       Недостатки языка Паскаль, связанные с возможностью управления последовательностью действий, являются хотя и не очень существенными, но зато многочисленными - это скорее смерть от множественных ран, чем от одного смертельного удара.
       В языке Паскаль отсутствует гарантированный порядок выполнения логических операций and и or, в отличие от логических операций && и || в языке Си. Этот недостаток свойственный многим языкам программирования, ооооемно заметен в операторах циклов, что иллюстрируется следующим примером:

while (i <= ХМАХ) and (х[i] > 0) do ...

       Использование в языке Паскаль операторов типа приведенного выше является крайне неблагоприятным: нет никакой гарантии, что проверка значения переменной i будет осуществлена до проверки значения элемента массива x[i].
       Кстати, необходимо отметить, что использование скобок в приведенном выше примере является обязательным, поскольку в языке Паскаль имеется всего четыре уровня приоритета операций, а операции отношения находятся на самом нижнем из них.
       В языке Паскаль отсутствует оператор завершения break, обеспечивающий возможность досрочного завершения выполнения операторов циклов и выхода из них. Это, конечно, находится в полном соответствии с одной из концепций структурного программирования (по которой наличие единственного входа и единственного выхода необходимо), но приводит к громоздкости или необходимости дублирования одних и тех же фрагментов программ, что усугубляется отсутствием гарантированного порядка выполнения логических операций. Рассмотрим в качестве примера ситуацию, часто встречающуюся на практике, которая легко может быть записана с помощью языков Ратфор и Си:

while (getnext (...)) {
   if (условие) break
остаток тела цикла
}

       Отсутствие оператора завершения break в языке Паскаль приводит к необходимости записать этот же фрагмент программы по-другому:

done := false;

while (not done) and (getnext (...)) do 
   if условие then
      done := true
   else begin
      остаток тела цикла

end

       Однако такое решение неверно: вследствие отсутствия в языке Паскаль гарантированного порядка выполнения логических операций нет никакой гарантии, что значение 'not done' будет проверено до обращения к getnext. Тогда после нескольких неудач можно прийти к следующему варианту:

done := false; 

while not done do begin
   done := getnext (...);
   if условие then
      done := true 
   else if not done then begin
      остаток тела цикла 
   end
end

       Конечно, "рецидивисты" могут прибегнуть к использованию оператора перехода goto и метки (естественно, числовой и предварительно описанной) для решения проблемы, связанной с преждевременным завершением операторов циклов и выхода из них. Короче говоря, при использовании языка Паскаль необходимость преждевременного завершения операторов циклов и выходов из них приводит к использованию искусственных и часто болезненных решений, связанных с необходимостью использования булевских переменных и некоторых других хитрых способов. Для сравнения рассмотрим задачу нахождения последнего отличного от пробела символа в некотором массиве символов. Вот решение, записанное на языке Ратфор:

for (i = max; i > 0; i = i - 1 )
   if (arr(i) != ' ') 
      break

       А вот решение, записанное на языке Паскаль:

done := false;
i := max;

while (i > 0) and (not done) do
   if arr[i] = ' ' then
      i := i - 1
   else
      done := true;

       В языке Паскаль вне оператора цикла for значение его управляющей переменной не определено, а значит невозможно определить, был ли выполнен оператор цикла for полностью или его выполнение было преждевременно завершено. Еще одним не очень существенным недостатком оператора цикла for в языке Паскаль является то, что зйачение шага ограничено лишь двумя случаями: +1 и —1.
       В соответствии с уже упоминавшейся концепцией структурного программирования, по которой необходимо наличие единственного входа и единственного выхода, в языке Паскаль отсутствует оператор return. Возвращаемое функцией значение предварительно присваивается псевдопеременной (как в языке Фортран). Это иногда приводит к громоздкости, так как необходимо, чтобы каждая из ветвей (по которым может быть вычислено возвращаемое функцией значение) обеспечивала переход к концу функции с соответствующим (правильно вычисленным) значением. В языке Паскаль отсутствует стандартный способ прекращения выполнения программы, кроме перехода на конец самого внешнего блока, хотя во многих реализациях трансляторов с языка Паскаль и введен оператор halt, вызывающий немедленное завершение выполнения программы.
       Оператор варианта case в языке Паскаль лучше, чем оператор переключатель switch в языке Си, но в нем отсутствует возможность указания альтернативы default и того (в языке Паскаль не определено), что должно происходить в случаях, если значение управляющего оператором варианта case выражения не совпадает со значением ни одного из вариантов. Эти недостатки оператора варианта case в языке Паскаль практически лишают его какой-либо стоимости. Так, среди более чем 6000 строк, написанных на языке Паскаль и приведенных в книге [100], оператор варианта case встречается всего 4 раза, хотя наличие в нем возможности указания альтернативы default увеличило бы число употреблений оператора варианта case по крайней мере в 3 раза. Стандарт ISO на язык Паскаль [2] не предполагает никаких изменений по затронутым здесь вопросам.

10.4. ОКРУЖЕНИЕ

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

write ('Please enter your names ');
read (name);
. . .

       Большинства неприятностей такого рода, вообще говоря, можно избежать путем чрезвычайно тщательной и аккуратной реализации подсистемы ввода-вывода, но, к сожалению, далеко не все реализации трансляторов с языка Паскаль имеют столь тщательно и аккуратно реализованные подсистемы ввода-вывода. Необходимо также отметить, что такая тщательная и аккуратная реализация подсистем ввода-вывода обходится совсем не бесплатно.
       Средства ввода-вывода в языке Паскаль являются отражением средств ввода-вывода, имеющихся в той операционной системе, в рамках которой была осуществлена самая первая реализация транслятора с языка Паскаль. Даже сам Н.Вирт признает тенденциозность средств ввода-вывода в языке Паскаль, не признавая, правда, их недостатков [174]. В результате ввода последнего символа некоторой строки символов стандартная встроенная функция (флаг) eoln приобретает значение "истина". Аналогично в результате ввода последнего символа файла стандартная встроенная функция (флаг) eof приобретает значение "истина". В обоих случаях, прежде чем продолжить ввод, необходимо проверить значение соответствующего флага.
       Понятно, что реализация (или эмуляция) посимвольного ввода становится весьма трудоемкой. Следующий пример иллюстрирует реализацию функции get с на языке Паскаль, корректно выполняющую необходимые действия при использовании первых двух упомянутых в разд.10.1 трансляторов с языка Паскаль, но не обязательно корректно выполняющую необходимые действия при использовании какого-либо другого транслятора с языка Паскаль:

{getc — ввод одного символа со стандартного ввода}

function getc (var с: character): character;

var
   chs char;

begin
if eof then
   с := ENDFILE
   else if eoln then begin
      readln;
      с := NEWLINE
   end
   else begin 
      read (ch);
      с := ord(ch)
   end;
   getc := с

end;

       Необходимо отметить, что тип character отличается от стандартного символьного типа в языке Паскаль, поскольку значение ENDFILE и, возможно, значение NEWLINE не являются допустимыми для переменных, имеющих стандартный для языка Паскаль символьный тип.
       В языке Паскаль практически отсутствуют средства взаимодействия с файловой системой, кроме весьма скромных возможностей, основанных на идеологии пакетной обработки в операционной системе для ЭВМ семейства CDC 6000, в рамках которой была осуществлена самая первая реализация языка Паскаль. Переменные, имеющие файловый тип:

var fv : file of тип

       представляют собой специфические объекты, которые не могут быть использованы никаким иным способом, кроме как обращения к стандартным встроенным процедурам и функциям eof, eoln, read, write, reset, rewrite (процедура reset, обеспечивающая "перемотку" файла, делает его вновь доступным для чтения, а процедура rewrite делает файл доступным для записи).
       В большинстве реализаций трансляторов с языка программирования Паскаль имеются, впрочем, различные средства,, обеспечивающие возможность доступа к файлам по именам, но средства эти, как правило, не очень удобны и, что самое главное, не стандартизованы. Так, во многих реализациях трансляторов с языка программирования Паскаль допускается обращение к стандартным встроенным процедурам reset и rewrite с указанием в качестве параметра имени файла:

reset (fv, иия_файла);

       Но, к сожалению, являясь процедурами, и reset и rewrite не возвращают никакого значения, которое могло бы быть использовано в качестве индикатора успешного или неуспешного выполнения ими своих функций, поэтому в языке Паскаль отсутствует способ, обеспечивающий обнаружение их безуспешного выполнения. А поскольку переменные, имеющие файловый тип, будучи весьма специфическими объектами, не могут использоваться следующим образом (в выражениях):

reset (fv, имя_файла);
if fv = failure then ...

       то из этой ситуации выхода нет. Такая "смирительная рубашка" делает практически невозможным написание программ, обеспечивающих восстановление и повторный запрос имени файла, в том случае, когда имя файла было введено с ошибкой, и в ряде аналогичных ситуаций. Автору не удалось решить эту проблему адекватным образом в процессе переработки книги [99].
       В языке Паскаль отсутствуют средства передачи в программу параметров, указанных в командной строке, обеспечивающей вызов этой программы на выполнение, что также является "наследственной" болезнью, имея в виду, что самая первая реализация транслятора с языка Паскаль была осуществлена в рамках операционной системы, обеспечивающей работу в режиме пакетной обработки. В конкретной реализации транслятора с языка Паскаль такие средства могут быть реализованы путем расширения окружения, в котором обеспечивается выполнение написанных на языке Паскаль программ, за счет добавления нестандартных процедур.
       Используя язык Паскаль, невозможно реализовать распределитель памяти общего назначения (в силу уже обсуждавшихся проблем, связанных с типами). Но в языке Паскаль имеется встроенная процедура new, обеспечивающая выделение памяти из "кучи" для размещения произвольных объектов. Однако это не обеспечивает, например, возможности выделения массива неопределенного размера для хранения произвольной строки символов. Ссылки, возвращаемые встроенной процедурой new, не могут использоваться в выражениях, поскольку в языке Паскаль отсутствует "ссылочная" (адресная) арифметика.
       Стандарт ISO на язык Паскаль {2] не предполагает никаких изменений ни по одному из затронутых здесь вопросов.

10.5. ВОПРОСЫ КОСМЕТИКИ

       Большинство из рассмотренных в настоящем разделе вопросов может показаться опытному программисту утомительным, а некоторые из них, возможно, покажутся таковыми даже неопытным программистам. Со всеми из рассмотренных в настоящем разделе недостатками языка Паскаль вполне можно примириться.
       В языке Паскаль, как и в большинстве языков программирования, являющихся "наследниками" языка Алгол 60, символ ';' используется в качестве разделителя операторов, а не в качестве завершителя операторов, как, например, в языках ПЛ/1 и Си. В результате, чтобы обеспечить правильность расстановки символов ';' в программе, написанной на языке Паскаль, требуется использовать достаточно сложное понятие о том, что является оператором. Возможно, что еще более важно, что тому, кто серьезно заботится о правильности расстановки символов ';' программы на язык Паскаль, потребуется довольно утомительное редактирование. В качестве примера рассмотрим фрагмент программы на языке Паскаль:

if a then
   b;
c;

       Если же теперь понадобится вставить что-либо перед оператором b, то нужда в символе ';' после b отпадает, поскольку в этом случае символ ';' должен быть поставлен после операторной скобки end:

if a then begin
  b0;
  b;

end;
с;

       Если же теперь понадобится вставить служебное слово else с последующим за ним оператором d, то символ ';' после операторной скобки end необходимо удалить:

If a thin begin
   b0;
   b
end
else
  d;
c;

       И так далее без конца - с появляющимися и исчезающими при внесении различных изменений многочисленными, встречающимися в различных местах написанной на языке Паскаль программе, символами';'.
       В одном из экспериментальных исследований по психологии программирования [53] было показано, что испспьзование символа ';' в качестве разделителя операторов увеличивает вероятность ошибки приблизительно в 10 раз по сравнению с использованием символа ';' в качестве завершителя операторов. Кстати, в языке Ада [89] — языке программирования, в значительной степени опирающемся на язык Паскаль, — символ ';' используется в качестве завершителя операторов. К счастью, при использовании языка Паскаль почти всегда можно закрыть глаза на функциональное назначение символа ';' и использовать его в качестве завершителя операторов. Этого нельзя сделать лишь в двух случаях: при описании разного рода объектов (в этом случае это не столь уж существенно) и непосредственно перед служебным словом else, так что эти два исключения дегко запомнить. Программисты, привыкшие использовать языки Си и Ратфор, находят операторные скобки begin и end слишком громоздкими по сравнению с операторными скобками { и }.
       Поскольку при использовании языка Паскаль имя функции служит для обращения к ней., отсутствует возможность различения имен функций и имен переменных, если только не помнить имен всех используемых функций. В языке Паскаль при этом используется трюк, впервые примененный в языке Фортран и заключающийся в использовании имени подпрограммы в качестве имени переменной. Только в языке Фортран имя подпрограммы является полным аналогом имени переменной и может быть точно так же использовано в выражениях. А в языке Паскаль использование функций в выражениях приводит к рекурсивному обращению к ним: так, если f — функция без аргументов, то f :=f + 1 приводит к рекурсивному обращению к функции f.
       В языке Паскаль можно отметить недостаточное число операций (возможно, связанное с недостаточный числом уровней приоритетов операций). В частности, в языке Паскаль отсутствуют побитовые операции (AND, OR, XOR и т.д.). Автору пришлось просто отказаться от попытки записать на языке Паскаль следующий фрагмент программы:

i := 1;
while getc (с) () ENDFILE do begin
   putc (xor (с, кеу[i]));
   i := i Mod keylen + 1 end

       в силу того, что ему не удалось сколько-нибудь приемлемым образом записать функцию XOR. В подобных случаях некоторую помощь может оказать использование типа множеств (по крайней мере многие утверждают, что это так), но эта помощь явно недостаточна. Те, кто утверждает, что язык Паскаль является языком для системного программирования, просто проглядели этот недостаток языка Паскаль. Поэтому, например, несколько наивно выглядит следующее утверждение, приведенное в опубликованной в 1977г. статье [167]:

В настоящее время язык Паскаль является лучшим из всех доступных языков программирования для системного программирования и реализации программного обеспечения".

       При использовании языка Паскаль отсутствует возможность использования символов, не имеющих графического представления внутри строк символов. На самом деле символы, не имеющие графического представления, являются в языке Паскаль "персонами нон-грата" в намного более широком смысле, поскольку в описании языка Паскаль [91] о них нигде ничего не сказано. Интерпретация символов типа 'newline' и 'tab' осуществляется в каждой реализации трансляторов с языка Паскаль по-своему и, как правило, с учетом используемого кода (EBCDIC или ASCII).
       В языке Паскаль отсутствуют пустые строки. Причина этого, возможно, заключается в том, что при необходимости использовать символ '" внутри некоторой строки символов он должен быть удвоен, что иллюстрируется следующим примером:

'символ '' содержится в этой строке символов'

       Для языка Паскаль отсутствует набор средств, реализуемых некоторым стандартным макропрепроцессором. Использование описателя const в языке Паскаль позволяет решить около 95% проблем, решаемых в языке Си с помощью конструкции #define, реализуемой препроцессором транслятора с языка Си. Однако для оставшихся 5% проблем удовлетворительное решение отсутствует. Конечно, не очень сложно добавить макропрепроцессор к транслятору с языка Паскаль. Это позволяет, например, эмулировать весьма удобную в использовании подпрограмму вывода сообщений об ошибках:

#define error(s) begin writeln(s); halt end

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

error ('короткое сообщение об ошибке')
error ('это несколько более длинное сообщение об ошибке')

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

const SIZE = 10;
type arr = array [1..SIZE + 1] of integer?

       или даже еще более простые описания:

const SIZE = 10;
      SIZE1 = SIZE + 1;

10.6. ЗАКЛЮЧЕНИЕ

       Попытки переписать программы с языка Ратфор из книги [99] на язык Паскаль были начаты в марте 1980г. и продолжались до января 1981г. В своем окончательном виде книга [100] была опубликована в июне 1981г. За этот период автор полностью приспособился к большинству не очень существенных проблем, связанных с использованием языка Паскаль (косметика и управление последовательностью действий), и разработал ряд не самых удачных решений для преодоления лаиболее существенных из них (массивы и окружение).
       Программы, написанные на языке Паскаль, приведенные в книге [100], являются завершенными и хорошо спроектированными и предназначены для решения нетривиальных задач. Но от этих программ не требовалось высокой эффективности, и способы взаимодействия с операционной системой, используемые в них, являются весьма примитивными. Это позволило автору применить ряд "прелестных" решений, которые, конечно же, не являются приемлемыми для реальных программ.
       Автору не удалось обнаружить сколько-нибудь существенного превосходства языка Паскаль над языком Си, но удалось отметить ряд особенностей языка Паскаль, позволяющих говорить о некотором его превосходстве над языком Ратфор. Наиболее очевидной из таких особенностей является рекурсия: ряд программ выглядит намного более ясно при записи этих программ с использованием механизма рекурсии. К таким программам относятся, например, программы поиска по образцу, быстрой сортировки, вычисления выражений.
       Хорошей идеей является введение перечислимых типов, позволяющих заранее ограничить диапазон допустимых значений. Использование записей облегчает группировку связанных друг с другом переменных. Автор обнаружил, что необходимость использования ссылок возникает крайне редко.
       Использование булевских переменных в условных выражениях намного удобнее, чем использование с той же целью целых переменных. Первоначальные версии программ, написанных на языке Ратфор, содержали ряд неестественных конструкций вследствие недостатков, присущих использованию булевских (точнее логических) переменных в языке Фортран.
       Механизм сильной типизации в языке Паскаль облегчает выявление разного рода описок, возникающих в процессе написания программ. Весьма полезны и проверки, осуществляемые на этапе выполнения, позволяющие, в частности, выявлять ошибки, связанные с выходом за границы массивов.
       Теперь о недостатках языка Паскаль.
       Перетрансляция всей программы в результате внесения изменений в одну из ее строк оказывается весьма утомительной. Наличие механизма раздельной трансляции с проверкой соответствия типов или без нее является просто обязательным для разработки больших программ.
       Автор ощутил очень маленькую выгоду или даже скорее практически полное ее отсутствие от того, что в языке Паскаль, в отличие от языка Фортран, имеется символьный тип. Это связано с двумя обстоятельствами: с тем, как в языке Паскаль трактуются строки символов, и с отсутствием возможности использования в языке Паскаль в строках символов символов, не имеющих графического представления. И в языке Фортран, и в языке Паскаль инициализация таблиц служебных слов, сообщений об ошибках и т. д. осуществляется чрезвычайно неуклюже.
       Функционально эквивалентные программы, написанные на языках Ратфор и Паскаль, имеют практически одинаковый размер, если измерять последний числом строк в программах. Поначалу этот факт удивил автора, поскольку первые впечатления автора от языка Паскаль были связаны с многословием и недостаточно высокой по сравнению с языком Ратфор выразительностью. По-видимому, получившийся практически одинаковым размер программ является следствием того, что при использовании языка Паскаль в заголовках операторов циклов могут быть применены выражения произвольной сложности, что недопустимо при использовании языка Фортран (автор имеет в виду язык Фортран 66). Это позволяет избавиться от ряда "бесполезных" операторов присваивания.
       В заключение автор просуммирует свои основные претензии к языку Паскаль:
       1. Поскольку размер массива является частью его типа, то оказывается невозможным написание подпрограмм общего назначения, в которых используются массивы произвольной длины. В частности, это очень сильно затрудняет работу со строками символов.
       2. Отсутствие статических переменных и средств инициализации переменных произвольными значениями на этапе трансляции приводит к неоправданному и вредному увеличению размеров областей действия.
       3. Однопроходная схема трансляции приводит к тому, что порядок, в котором записываются в программе процедуры и функции, становится неестественным. Принудительное отделение области описаний объектов приводит к рассеянию по программе логически связанных друг с другом объектов.
       4. Отсутствие механизма раздельной трансляции существенно затрудняет разработку больших программ и делает практически невозможнымиспользование библиотек.
       5. Отсутствие контроля за вычислением логических выражений и гарантированного его порядка приводит к увеличению размера программ и необходимости использования дополнительных переменных.
       6. Оператор варианта case сильно ослаблен в результате отсутствия возможности указания альтернативы defauft.
       7. Средства ввода-вывода очень неудачны и неудобны. Отсутствуют удобные средства для работы с файлами, средства передачи в программу параметров, указанных в командной строке, обеспечивающей вызов этой программы на выполнение.
       8. Отсутствуют средства, необходимые для создания больших программ, и в первую очередь механизм включения файлов.
       9. Выхода нет.
       Последний пункт, быть может, наиболее важный из всех перечисленных выше. Дело в том, что, обладая большим числом недостатков, язык Паскаль очень замкнут, что не позволяет избежать большинства этих недостатков различными окольными путями. В частности, в языке Паскаль отсутствует механизм приведения типов, необходимый для контролируемого нарушения механизма сильной типизации. При использовании языка Паскаль оказывается практически невозможным заменить одно окружение, в котором обеспечивается выполнение программ, на другое, более удобное по тем или иным соображениям. Единственным способом сделать это является овладение контролем над транслятором с языка Паскаль.
       Программисты, использующие язык Паскаль для решения реальных задач, попадают в ловушку: язык Паскаль настолько ограничен, что требует весьма серьезных расширений. Но в каждом таком случае расширение языка Паскаль осуществляется в различных направлениях (определяемых спецификой предметной области) и различным образом с целью придать языку Паскаль облик, соответствующий модели языка программирования, имеющейся в голове у тех, кто занят соответствующим расширением. Расширения языка Паскаль путем введения механизма раздельной трансляции, механизма общих (COMMON) блоков "а-ля" язык Фортран, типа данных строка символов, статических переменных, средств инициализации объектов произвольными значениями на этапе трансляции, восьмеричных констант, побитовых операций и т.д. облегчают решение задач, стоящих перед теми, кто производит соответствующие расширения, но полностью ликвидируют возможность переноса программ, в которых используются эти расширения.
       По мнению автора, использование языка Паскаль в любом ином качестве, кроме того, которое имелось в виду при его создании — обучение программированию, является ошибкой. В "чистом" виде (т.е. без каких-либо расширений) язык Паскаль является "игрушечным" языком программирования, пригодным для обучения программированию, но не пригодным для написания реальных программ.

10.7. БЛАГОДАРНОСТИ

       Автор благодарен А.Ахо, А.Фьюэру, Н.Джехани, Б.Мартину, Д.Макилрою, Р.Пайку, Д.Ритчи, К. ван Уайку и Ч.Уэзереллу за ценные замечания, сделанные ими к предварительным вариантам настоящей статьи.




<<< Пред. Оглавление
Начало раздела
След. >>>

Дата последнего изменения:
Thursday, 21-Aug-2014 09:10:56 MSK


Постоянный адрес статьи:
http://az-design.ru/Support/SoftWare/Delphi/Pascal/001b3010.shtml