Основная задача языка программирования - помочь программисту
в совершенствовании его искусства.
Хоар
С точки зрения программирования нас интересуют все те языки, которые участвуют
в создании программы. На рис. 4.1 схематически указаны такие языки.
Рассматривать язык программирования можно с нескольких позиций. Перечислим те из
них, которые будут нам особенно полезны [Кауфман 1986].
Технологическая позиция, отражающая взгляд человека, желающего или вынужденного
пользоваться языком программирования как технологическим инструментом на каком-либо
из этапов жизненного цикла программного обеспечения. Эта позиция использовалась в
гл. 3.
Семиотическая позиция, отражающая особенности языка как знаковой системы. Эта
позиция доминирует в данной главе.
Математическая позиция, представляющая математическую модель изучаемого языка.
Реализаторская позиция, отражающая взгляд на язык реализаторов транслятора с
этого языка и авторов документации. Эта позиция используется в гл. 5.
Авторская позиция, отражающая позицию автора языка. Для понимания этой позиции
следует обращаться к работам авторов языков программирования. Например, точка зрения
Никлауса Вирта отражена в статье [Вирт 1996].
Основные понятия и определения
1.1.1. Языки и знаки
Для информации, которой обмениваются люди посредством сообщений, в большинстве
случаев имеются соглашения относительно их формы. Сообщения составляются на некотором
языке с помощью знаков.
Введем простое определение знака [Лукин 1999]. Форма знака всегда материальна и
передается через ощущения человека. Это может быть образ оптический (например, написанное
слово), акустический (например, звук выстрела), обонятельный (например, запах свежего
миндаля) или тактильный (например, поцелуй). Обычно нас интересует не форма знака,
а то, о чем он сообщает - некий предмет, явление, процесс, идея. Замещая свою форму
или представляя нечто отличное от нее, знак тем самым сообщает информацию. Заметим,
что, говоря о представлении замещаемого предмета, мы обычно имеем в виду не сам предмет,
а типичный образ, класс таких предметов. Этот представитель класса называется денотатом.
Денотаты со своими формами связаны ассоциацией - значением знака - отражением денотата
в виде множества содержательных признаков, связывающего его с формой знака. Форма,
значение и денотат образуют структуру знака, которую обычно изображают в виде "треугольника
Фреге" (рис. 4. 2).
Пунктирная линия, соединяющая форму и денотат, означает, что они соотносятся между
собой не непосредственно, а через значение. Денотат отражается в значении, значение
придает знаковость форме и форма вследствие этого представляет денотат.
Модель передачи сообщений
Одна из основных задач, стоящих перед программистом, - добиться взаимопонимания
с компьютером. Чтобы разобраться в возникающих при этом проблемах, рассмотрим модель
передачи сообщений [Кауфман 1986] (рис. 4. 3).
В этой модели присутствуют два основных действующих лица.
Отправитель (он же автор, генератор сообщения).
Адресат (он же получатель, читатель, слушатель сообщения).
Собственно сообщение представляется последовательностью знаков (например, текст
или ряд звуков). Его смысл - нечто, обозначаемое сообщением, в соответствии с правилами,
известными и отправителю и адресату.
Укажем, что изучают науки, которые фигурируют в этой модели.
Синтаксис - правила построения допустимых сообщений.
Семантика - правила сопоставления смысла таким сообщениям.
Прагматика - правила, регулирующие использование сообщений.
Известны языки разговорный и письменный, язык жестов и мимики, язык для слепых,
воспринимаемый осязанием.
О языке влюбленных
Отличительными признаками диалога влюбленных являются: рыхлый синтаксис, могучая семантика
и полное отсутствие прагматики (http://asu.pstu.ac.ru/structure/prepod/solov.htm).
С помощью приведенной модели передачи сообщений можно классифицировать возникающие
при этом недоразумения [Кауфман 1986].
Синтаксические недоразумения. Отправитель может подразумевать одну структуру
сообщения, а адресат - другую. Пример - классический королевский указ - "Казнить нельзя
помиловать!". Кстати, добрый текстовый редактор (Microsoft Word), с помощью которого
создавалась книга, при анализе этой фразы выдает рекомендацию "Возможно, не хватает
запятой после слова "нельзя"".
Семантические недоразумения. Отправитель и адресат могут по-разному понимать
некоторые слова. Пример - фраза на пакетике риса - "варить до готовности".
Прагматические недоразумения. Адресат может придать сообщению отправителя совершенно
другую интерпретацию. Примером является огромное количество анекдотов, посвященных
так называемой "женской" логике.
О деформации знака в случае затрудненного восприятия
Получатель знака может деформировать его структуру, превращая один вид знака в другой.
Это типично для ситуаций, когда имеет место восприятие, затрудненное по тем или иным
причинам. В таких ситуациях получатель стремится свести непонятное - к уже знакомому
и ясному для него [Лукин 1999]: "Куприна, уже подвыпившего, раз привели к Балтрушайтису,
чтобы представить: "Знакомьтесь: Куприн, Балтрушайтис". Куприн же: "Спасибо: уже балтрушался".
Ему показалось спьяну глагол "балтрушайтесь" - в значении понятном весьма: "Угощайтесь"".
Добавим, что при анализе сообщений на естественном языке могут возникнуть многие
другие проблемы. Например, при омонимии у одного знака могут быть несколько пар значение-денотат.
Хрестоматийный пример - "закрой замок на замок, чтобы замок не замок".
Языковые знаковые системы
Семиотика - наука, изучающая языковые знаковые системы.
Язык вообще - определенный способ сопоставления объектам Ri, которые рассматриваются
как некоторая первичная реальность, объектов Li, называемых именами Ri и рассматриваемых
как нечто вторичное, специально созданное для сопоставления объектам Ri [Турчин 2000].
По отношению к своему имени Li объект Ri называют его значением. Совокупность всех
объектов Li также часто называют языком.
Отметим две основные функции языка.
Коммуникативная функция. Это первая функция языка, поскольку он возникает как
средство связи.
Моделирующая функция. Язык используется в качестве средства построения моделей
действительности.
Интересное и образное определение языка принадлежит Тузову [Тузов 1990]. Язык -
средство описания информационных процессов. Предложения языка и текст - застывшие
формы процессов. Они превращаются в активный процесс во время выполнения его человеком
или вычислительной машиной.
Для человека естественно линейное и последовательное построение предложений языка.
Однако существуют и более сложные способы построения предложений. Например, марсианская
грамматика (http://progstone.nm.ru) имеет свои
правила и использует Северный голос для эмоций, Южный голос для действия, Восточный
голос для речи и Западный голос для обстоятельств. На Марсе есть марсиане, едящие
мышей и мыши, скрывающиеся среди скал и едящие скалы. Марсиане записывают свою литературу
на больших скалах с помощью маленьких обломков скал. Наиболее близкий марсианский
аналог нашего "Преступления и наказания" (написанного в 1866 году Ф. М. Достоевским
[Достоевский 1985]) называется "Кто украл мою мышь?" (табл. 4.1).
Таблица 4.1. Фрагмент марсианской литературы
Эмоция
Действия
Речь
Обстоятельства
Сердитый
Подкрадывается
Ужасный марсианин
Холодный, мрачный
Определенно
Бить
Умри! Умри! Умри!
Старая пещера
Стыдно
Украсть мышь
Мертвый марсианин
Неожиданный пробел в Восточном голосе - непроизнесенное проклятие, очень реальное
само по себе. Обратим внимание на то, что линейное использование последовательностей
символических преобразований, хорошо понятное человеку, становится непонятным марсианам,
мозг которых позволяет почувствовать сложные отношения между независимыми действиями.
Об универсальном языке
В мире существует огромное количество языков - и естественных, и моделирования,
и программирования. Известно, что в начале XX века был изобретен универсальный естественный
язык эсперанто. Он был специально разработан так, чтобы облегчить его изучение. Конец
этой истории известен - в наше время эсперанто знают единицы, а тысячи языков и диалектов
звучат и не собираются исчезать. В мире языков программирования были следующие попытки
разработать и внедрить универсальный язык:
язык PL/1 был создан в компании IBM в конце 60-х годов XX века;
язык Algol-68 являлся итогом многолетней работы (1963-1968 гг. ), проведенной
Международной Федерацией по обработке информации;
язык Ada был создан по заказу министерства обороны США в конце 70-х - начале
80-х годов XX века.
Ни одна из этих попыток не достигла успеха. Объяснить эту ситуацию можно с помощью
гипотезы Хурфа-Сэпира, утверждающей, "что наш язык, наш словарь, словесные формы,
контролируют и ограничивают мышление, обусловливая нашу способность мыслить" [Васкевич
1996].
То - что мы произносим - это то, о чем мы думаем. Пусть в языке какого-то народа
отсутствуют, например, слова, отражающие само понятие времени (или понятия снега,
пустынь и т. п. ). Даже если этот народ перейдет к другому языку, в котором такие
слова есть, он не сможет их использовать, поскольку не умеет думать об этих понятиях.
Необходимость существования многих языков и систем обозначений порождается принципом
расслоения абстракции. Человек не может напрямую работать со сложными системами, одновременно
отслеживать множество взаимосвязанных элементов. Слои абстракции позволяют людям понизить
сложность систем в мире, математике или программировании до управляемых размеров.
Формализация языка
О железнодорожном языке
"Платформу Красные Зори поезд проследует без остановки". Обратим внимание, что машинист
употребил существительное "остановка", а не глагол "останавливаться". Остановка -
очень важное для железнодорожников понятие. Поезд может "остановиться", но не "иметь
остановки". Турчин [Тур-чин 2000], приводя подобный пример, указывает на формализацию
языка, употребляемого в узких профессиональных целях.
Формализованный язык можно определить следующим образом [Турчин 2000]. Рассмотрим
двухэтажную языковую модель действительности (рис. 4. 4). Ситуация si кодируется языковым
объектом Li. Объект L1 есть имя для si. Некоторое время спустя ситуация S1 сменяется
ситуацией S2. Осуществляя некоторую языковую деятельность, преобразуем L1 в другой
объект - L2. Если наша модель правильна, то L2 есть имя S2. В результате, не зная
реальной ситуации S2, мы можем получить представление о ней путем декодирования языкового
объекта L2. Выполнение преобразования L1->L2 определяет, будет ли язык формализованным.
Для формализованного языка преобразование L1->L2 определяется исключительно языковыми
объектами Li, которые участвуют в нем и не зависят от языковых представлений si, соответствующих
им по семантике языка.
Для неформализованного языка результат преобразования языкового объекта Li зависит
не только от вида самого представления Li, но и от представления si, которое он порождает
в голове человека, от ассоциаций, в которые он входит.
Человек способен воспринимать самые неформализованные языки. А компьютер не понимает,
точнее, не может исполнить программу на неформальном языке. Именно поэтому важное
место в изучении программирования всегда занимают формальные алгоритмические языки
программирования,
О формализации неформализованного
Формализация неформализованного - процесс неформализуемый. Хотя с этим пытаются бороться
логики и военные. О формуле любви
Формула любви не поддается формализации. В лучшем случае она может быть представлена
только в виде весьма грубой модели (http://asu.pstu.ac.ru/structure/prepod/solov.htm).
1.1.2. Языки моделирования
Язык моделирования - набор правил, определяющих построение моделей (упрощенного
представления реальности), включающий их визуализацию и определение структуры и поведения.
Язык моделирования включает:
элементы модели - фундаментальные концепции моделирования и их семантику;
нотацию - визуальное представление элементов моделирования;
руководство по использованию - правила применения элементов в рамках построения
моделей предметной области.
1.1.3. Языки программирования высокого уровня
Язык программирования (алгоритмический язык) - набор правил, определяющих, какие
последовательности символов составляют программу (синтаксические правила) и какие
вычисления описывает программа (семантические правила).
Программа - текст, задающий множество процессов вычислений, в соответствии с которым
исполнитель, понимающий программу, разворачивает какой-то один из них.
Выделим три основные характеристики языков программирования [Калинин, Мацкевич
1991].
Уровень языка - характеризуется сложностью задач, решаемых с помощью этого языка.
О том, чем определяется уровень языка программирования
Интуитивно любой программист отличит язык программирования высокого уровня от языка
программирования низкого уровня. В чем же состоит различие? Чем определяется уровень?
Программирование представляет собой отображение в программах объектов, понятий и явлений
предметной области задачи. Чем более адекватно можно выполнить это отображение, тем
выше уровень языка программирования. А отображение будет выполнено тем лучше, чем
богаче возможности типообразования языка программирования.
Мощность языка - характеризуется количеством и многообразием задач, алгоритмы
решения которых можно записать, используя- это* язык.
О взаимосвязи уровня и мощности языка
Чем выше уровень языка, тем ниже его мощность и наоборот.
Концептуальная целостность - характеризуется свойствами совокупности понятий,
служащих для описания этого языка и включает три взаимосвязанных аспекта.
Экономия понятий - язык должен достигать своей Максимальной мощности минимальным
количеством понятий.
Ортогональность понятий - между понятиями не должно быть взаимного влияния. Если
понятие используется в различных контекстах, то правило его использования должно быть
одним и тем же.
О неортогональности понятий даже самых лучших языков
К сожалению, даже такие языки, как Pascal, допускают неортогональные конструкции.
Например, пользователь может определить процедуры только с фиксированным числом параметров,
однако некоторые стандартные процедуры (например, writeln()) могут быть вызваны с
переменным числом параметров.
Единообразие понятий - требование согласованного единого подхода к описанию и
использованию всех понятий.
Существует большое количество других характеристик.
Надежность - язык должен обеспечивать минимум ошибок при написании программ.
Более того, язык должен быть таким, чтобы неправильные программы было трудно писать.
Удобочитаемость - легкость восприятия программ человеком. Это характеристика
важна при коллективной работе, когда несколько человек работают с одними и теми же
текстами программ.
Полнота - характеризует способность описать класс задач в некоторой предметной
области.
Отметим два свойства, которые не влияют на процесс разработки, но играют важную
роль, отражая реализаторскую позицию.
Мобильность - независимость от аппаратных средств, обеспечивающая переносимость
программного обеспечения.
Эффективность - обеспечение эффективной реализации, которая включает:
эффективную реализацию компилятора;
эффективную генерацию компилятором кода программ.
И это только некоторые из свойств и характеристик. В одном из докладов военно-морскому
флоту [Фоке 1985] исследователи насчитали более 2570 различных возможностей или свойств,
которые можно выделить в языках программирования.
О конфликтовании характеристик языков
Следует заметить, что многие из перечисленных характеристик конфликтуют друг с другом.
Например, надежность и эффективность. Это подчеркивает сложности, с которыми приходится
сталкиваться создателям новых языков.
1.2. История и эволюция
Покрывало алтаря в одной эпохе - это коврик для ног в следующей. М. Твен
1.2.1. История и эволюция языков моделирования
Принято выделять всего два поколения языков моделирования.
Первое поколение: Языки структурного системного анализа и проектирования. Появились
в середине 70-х годов XX века.
Второе поколение: Объектно-ориентированные языки моделирования. Первые языки
появились в конце 70-х годов XX века, однако их массовое появление и развитие приходится
на начало 90-х годов этого века.
Укажем некоторые важные даты, связанные с языками моделирования.
1976 г. - Ченом разработана диаграмма сущность-связь.
1995 г. - Консорциум OMG признает целесообразность поиска индустриальных стандартов
в области языков моделирования.
1996 г. - Появление первых документов, содержащих описание языка UML.
1997 г. - Публикация документа с описанием языка UML версии 1.0.
1.2.2. История и эволюция языков программирования
К сегодняшнему дню насчитывают шесть поколений языков программирования. Каждое
из последующих поколений по своей функциональной мощности качественно отличается от
предыдущего.
Первое поколение: Машинные языки. Появились в середине 40-х годов XX века.
Второе поколение: Ассемблеры. Фактически это те же машинные языки, но более красиво
"обернутые". Появились в конце 50-х годов XX века
Третье поколение: Процедурные языки. Появились в начале 60-х годов XX века. К
этому поколению относят универсальные языки высокого уровня, с помощью которых можно
решать задачи из любых областей (например, Algol-60).
Четвертое поколение: Языки поддержки сложных структур данных (например, SQL).
Появились в конце 60-х годов XX века.
Пятое поколение: Языки искусственного интеллекта (например, Prolog). Появились
в начале 70-х годов XX века.
Шестое поколение: Языки нейронных сетей (самообучающиеся языки). Исследовательские
работы в этой области начались в середине 80-х годов XX века.
Даты создания многих языков программирования будут указаны в разд. 4.5. Здесь мы
приведем лишь языки, которые считаются первыми: '
1945 г. - Конрадом Цузе (Conrad Zuse) разработан первый язык программирования
Plankalkuel;
1949 г. - разработанный Джоном Мочли (John Mauchly) язык Short Code американцы
считают первым языком программирования высокого уровня.
1.3. Классификация языков
1.3.1. Классификация языков моделирования
Языки моделирования могут быть разделены на три группы.
Языки описания архитектур (Architecture Description Language) - языки, предоставляющие
средства для моделирования концептуальной программной архитектуры. Их также называют
языками проектирования "в большом". Основными элементами таких языков, как правило,
являются:
компоненты, для которых могут быть определены поддерживаемые ими интерфейсы (порты);
соединители, реализующие протоколы взаимодействия компонентов и также поддерживающие
определенные интерфейсы (роли);
архитектурные конфигурации, которые являются композицией компонентов и соединителей
и могут быть представлены в виде иерархической структуры;
ограничения на композицию компонентов и соединителей в рамках архитектурных конфигураций.
Языки проектирования модулей (языки проектирования "в малом").
Языки спецификаций.
Отдельно следует выделить еще две группы языков моделирования, имеющих большое
значение для многих областей программирования.
Языки моделирования данных (см. разд. 4.4.3).
Языки моделирования знаний (см. разд. 4.4.4).
Эти две группы являются вспомогательными языками с точки зрения первых трех групп.
Например, некоторый язык моделирования данных может быть составной частью некоторого
языка спецификаций.
1.3.2. Классификации языков программирования
В настоящее время сложилось довольно много классификаций языков программирования.
Далее мы приведем самые известные из них.
Классификация по поддерживаемым методологиям
Классификация языков по поддерживаемым методологиям появилась примерно в 80-х годах
XX века. Мы выделили следующие основные группы языков:
языки императивного программирования (см. разд. 2.2.1) и две важнейшие подгруппы:
языки структурного императивного программирования (см. разд. 2.3.1). Эти языки
более известны под кратким именем - языки структурного программирования;
языки императивного параллельного программирования (см. разд. 2.4.1). Эти языки
также обычно называют кратко - языки параллельного программирования;
языки объектно-ориентированного программирования (см. разд. 2.2.2);
языки функционального программирования (см. разд. 2.2.3);
языки логического программирования (см. разд. 2.2.4);
языки программирования в ограничениях (см. разд. 2.2.5).
Классификация по принадлежности к семействам
Основная задача классификации языков по принадлежности к семействам - проследить
их родственные взаимосвязи (фактически, построить генеалогическое дерево) с целью
выяснения их влияния друг на друга и, следовательно, на характеристики и свойства
языков. Это нечеткая классификация, которая может вызвать спорное отношение к ней.
Выделим девять основных семейств.
Семейство универсальных языков (см. разд. 4.5.1.1).
Семейство уникальных языков (см. разд. 4,5.1.2).
Семейство языков параллельного программирования (см. разд. 4.5.1.3).
С-семейство языков (см. разд. 4.5.1.4).
Pascal-семейство языков (см. разд. 4.5.1.5).
Ada-семейство языков (см. разд. 4.5.1.6).
Simula-семейство языков (см. разд. 4.5.1.7).
Lisp-семейство языков (см. разд. 4.5.1.8).
Prolog-семейство языков (см. разд. 4.5.1.9).
Классификация по ориентации на предметные области
Язык программирования может быть специализированным для некоторой предметной области.
Выделим девять таких областей, хотя конечно их гораздо больше.
Языки форматирования текстов (см. разд. 4.5.2.1).
Языки разметки (см. разд. 4.5.2.2).
Языки скриптов (см. разд. 4.5.2.3).
Промежуточные языки программирования (см. разд. 4.5.2.4).
Языки программирования агентов (см. разд. 4.5.2.5).
Языки создания графики (см. разд. 4.5.2.6).
Языки описания аппаратуры (см. разд. 4.5.2.7).
Языки описания виртуальной реальности (см. разд. 4.5.2.8).
Языки конфигурирования (см. разд. 4.5.2.9).
Классификация по степени абстракции от аппаратуры
По степени абстракции от машины можно выделить три группы языков.
Языки низкого уровня. Такие языки имеют простые машиноподобные команды и осуществляют
прямой доступ к памяти. Пример - ассемблер для любой архитектуры.
Языки высокого уровня. Языки предоставляют возможность определять сложные структуры
данных, доступ к памяти осуществляется через операции. Примерами языков этого уровня
являются Pascal, С и Ada.
Языки сверхвысокого уровня. Команды исполняются на полностью абстрактной машине,
полностью скрыт доступ к памяти. Пример таких языков - Prolog, SETL, APL, Miranda.
1.4. Проблемы и перспективы развития
Через пять лет у нас будет один суперязык программирования, только
мы не можем установить начало этого пятилетнего периода. Алан Дж. Перлис
Тезисно сформулируем основные проблемы и перспективы языков программирования:
Во-первых, следует отметить, что новые языки программирования будут появляться
и дальше. Рано или поздно у нас возникнут кардинально новые мысли о программировании
и, согласно гипотезе Хурфа-Сэпира, они потребуют новых языков.
Во-вторых, следует обратить внимание на грамотное воплощение идей, заложенных в
языках. Идея может быть хорошей, даже отличной, но ее решение и воплощение в конкретном
языке - неудачным и безобразным.
Некоторые выводы о распространении новых языков можно сделать на основе языка Java
[Богатырев 1998].
Продвижение языка Java происходило за счет мощной рекламы, а не из-за достоинств
самого языка.
На широкое распространение в наше время могут претендовать только те языки, которые
поддержаны крупными фирмами, а не те, которые являются лучшими.
Время распространения и становления языка программирования (для получивших известность
языков) составляет в среднем от 3 до 10 лет. В случае Java язык получил широкое распространение
за один год.
В третьих, в условиях застоя в развитии языков программирования есть смысл тщательно
проанализировать все полезные накопленные за это время идеи. Существуют попытки создания
единой семантики современных языков программирования, в каком-то смысле опять приводящие
к идее "универсального" языка [Андреев 2001].
В четвертых, Интернет требует языковых средств и подходов, обеспечивающих правильное
взаимодействие большого числа независимо разработанных программ.
В пятых, использование структур и данных, которые раньше бы считались неприемлемыми
из-за их неэффективности, сейчас вполне допустимо и может привести к новой организации
языков. .,
Общий, несколько обнадеживающий итог звучит так. Ряд известных фирм и компаний
(например, Microsoft, Sun Microsystems и т. п.) постепенно приходит к идеям, заложенным
несколько десятков лет назад командой под руководством Никлауса Вирта.
1.5. Рекомендации по литературе
Горе тому, кто читает только одну книгу.
Джордж Герберт
Выделим следующие работы, посвященные анализу и обзору языков программирования:
"Языки программирования. Практический сравнительный анализ" [Бен-Ари 2000]. Это
один из лучших современных учебников по языкам программирования;
"Основные концепции языков программирования" [Себеста 2001]. Книга, содержит
описание фундаментальных понятий программирования на примере вопросов разработки различных
языковых конструкций;
"Языки программирования: разработка и реализация" [Пратт 1979]. Книга дает прекрасный
подход к анализу языков на основе реализаторской позиции;
"Универсальные языки программирования. Семантический подход" [Калинин, Мацкевич
1991]. В книге обобщены и систематизированы требования к современным языкам программирования;
"Языки программирования: концепции и принципы" [Кауфман 1986]. Книга содержит
основные принципы оценки, создания и использования современных языков программирования;
"A Critique of C++ and Programming and Language Trends of the 1990s". Работа
Яна Джойнера (Ian Joyner) (http://www.elj.com/cppcv3/)
представляет собой отличное введение в языки программирования и их сравнительный анализ;
"Феномен науки: кибернетический подход к эволюции" [Турчин 2000]. Автор прослеживает
эволюцию мира от простейших одноклеточных организмов до возникновения языка, мышления,
развития науки и культуры.
Две следующие книги содержат информацию о языках моделирования:
"Проектирование программного обеспечения экономических информационных систем"
[Вендров 2000] - дает представление о многих языках моделирования;
"Самоучитель UML" [Леоненков 2001] - содержит описание языка моделирования UML.
Знаете ли Вы, что конструкторы - это операции, которые используют в качестве аргументов объекты соответствующего им типа и создают другие объекты такого же типа. Например, операция сложения матриц создает новую матрицу.