В [1, с.195] приведена следующая классификация языков и стилей программирования:
Рис.1. Классификация языков программирования
В [2, с.11] мы встречаемся с похожей классификацией:
Рис.2. Классификация языков программирования
Причем, автор отмечает, что приведенная классификация не претендует на полноту, "существуют и другие парадигмы: например, параллельное, потоковое, продукционное программирование и более мелкие, основанные на конкретном методе".
Мы попытаемся расширить и упорядочить приведенные классификации. Ясно, что при любой классификации, удовлетворяющей логическим правилам деления, требуется с самого начала выбрать основание деления. Мы выберем в качестве основания деления парадигму программирования, а точнее - семантику (модель вычислительного процесса).
Заметим, что при построении классификационных систем специалисты в разных областях науки все более используют принцип развития, т.е. кладут в основу той или иной классификации развитие высших форм исследуемых объектов из низших [3, с.140]. Это позволяет предотвратить случайности и вообще многие неправильности в классификации изучаемых предметов. Однако такой метод сам по себе, конечно, не может автоматически привести к правильной классификации.
Вначале заглянем в толковые словари по информатике и вычислительной технике [4-7].
Язык программирования (Programming Language) - формальный язык представления программ или их частей в рамках одной или многих систем программирования. Число "живых", т.е. употребляемых ныне, языков программирования измеряется сотнями. В связи с исполнителем принято различать машинные языки и языки программирования высокого уровня. По характеру семантики (т.е. модели вычислительного процесса) в языках программирования можно выделить два основных наклонения - повелительное (императивное, представленное операторами, командами, предписаниями) и изъявительное (декларативное, описательное). В одних языках программирования преобладает описание действий, алгоритма, т.е. процесса, позволяющего получить результат. Такие языки называют императивными, (FORTRAN, BASIC, ALGOL, PL/1, PASCAL, C, ADA). Другие языки программирования предполагают не только построение (вычисление) результата, сколько описание (декларацию) его свойств; на основе этой информации система программирования сама должна построить алгоритм. Такие языки называют декларативными, непроцедурными, проблемно-ориентированными; языками отношений, спецификаций, формулировки задачи, языками искусственного интеллекта, автоматического программирования, синтеза алгоритмов.
Наиболее существенными классами декларативных языков являются функциональные (или аппликативные), продукционные и логические языки. К категории функциональных языков относятся, например, LISP, FP, APL, Nial, Krc и LOGO, к категории продукционных - Рефал. Самым известным языком логического программирования является PROLOG.
На практике языки программирования не являются чисто императивными, функциональными или логическими (подобные языки называют метафорами (см.[8, с.23])), а содержат в себе черты языков различных парадигм. На процедурном языке часто можно написать функциональную программу или ее часть и наоборот.
Отметим, что наряду с термином "декларативное программирование" можно использовать термин "логическое программирование".
Дело в том, что термин "логическое программирование", появившийся примерно в 1975 г., толкуют по-разному.
При узком толковании его связывают прежде всего с системами программирования, основанными на использовании специальных классов логических формул (хорновских дизъюнктов) в качестве логических программ и специальных методов логического вывода (вариантов метода резолюций) в качестве логической модели вычислений или способа исполнения логических программ. Поэтому логическое программирование в узком смысле иногда называют хорновским, резолюционным или "прологообразным" программированием, хотя каждый из этих эпитетов требует оговорок, так как схватывает только часть предмета [9, с.299].
При более широком толковании в логическое программирование включают гораздо больший круг понятий, методов, языков и систем, в основе которого лежит идея описания задачи совокупностью утверждений в некотором формальном логическом языке и получение решения построением вывода в некоторой формальной (дедуктивной) системе [9, с.299].
Классы формул, используемые для описания задач, методы определения их семантики, модели вычислений, основанные на тех или иных системах вывода или преобразования формул, очень разнообразны. Они образуют специфические "стили", "виды" или парадигмы программирования, которые существенно отличаются от традиционных, основанных на моделях вычислений, которые воплощены в традиционных языках программирования и в архитектуре типичных вычислительных машин первых четырех поколений (ее иногда называют фон-неймановской). Кроме хорновского (и/или резолюционного) программирования можно говорить об эквациональном программировании, функциональном (аппликативном) программировании и других "программированиях", а также о разных их комбинациях, объединяемых термином "логическое программирование".
Таким образом, пока мы можем выделить две парадигмы самого нижнего уровня дерева парадигм программирования:
Рис.3. Парадигмы нижнего уровня
Можно указать прообразы указанных типов алгоритмических языков в естественных языках. Для императивных языков это повелительное наклонение (императив, приказание), для сентенциальных - изъявительное наклонение (описание, повествование). Обращаясь к естественному языку, нетрудно заметить, что "изъявительное наклонение является несравненно более распространенным и образует, в сущности, основу языка, в то время как повелительное наклонение представляет в виде некоторой специальной модификации" [10]. Таким образом, можно сделать вывод о том, что "относительный вес изъявительного наклонения является мерой развитости языка"[10].
Перейдем к рассмотрению второго уровня парадигм программирования.
Рассмотрим парадигму программирования, не ориентированного на объекты. Эта парадигма возникла на заре становления вычислительной техники, имеет длительную историю развития и, долгое время будучи господствующей, постепенно уступает свое место объектно-ориентированной. Она распространяется на все слои программного обеспечения: от операционных систем (управление процессами виртуальной машины) до прикладного уровня. Информатика как наука развивалась именно под ее влиянием.
Как создается парадигма? Она формируется на этапе конструирования вычислительной среды, в том числе языков программирования высокого уровня. Основной фактор, учитываемый при конструировании вычислительной среды, - легкость и гибкость отображения предметной области решаемых задач на вычислительные средства.
Парадигма программирования, не ориентированного на объекты, базируется на представлении об управлении машиной со стороны программы или набора процедур (подразумевается, что машина может быть абстрактной, например, Pascal-машина).
Характерная взаимосвязь элементов в парадигме показана в виде диаграммы [11, с.15]:
Рис.4. Взаимосвясь элементов в парадигме
Выполнение процедур естественно назвать процессом, причем из одного процесса может активизироваться другой, что является главным способом управления общим процессом. В момент активизации может производиться передача данных, которые носят в таком случае название параметров. Окончание выполнения процедуры связано с возвращением результата и функции управления в вызывающую программу.
Обратим внимание на следующие недостатки, присущие парадигме программирования, не ориентированного на объекты [11, с.15]:
Отсюда можно сделать выводы, что более подходящим инструментом формализации знаний на ЭВМ была бы вычислительная среда, основанная на другой парадигме, базирующейся на концепции, которая приближена к понятию системы, или модели системы, получившей общедисципленарное распространение.
Такая постановка вопроса приводит к парадигме объектно-ориентированного программирования, в основе которой лежит идея активности данных, а не процедур, в чем можно усмотреть обращенность новой парадигмы по отношению к вышерассмотренной. Изобразим схематично диаграмму новой парадигмы [11, с.15]:
Рис.5. Диаграмма объектно-ориентированной парадигмы
На первый взгляд, приведенные диаграммы выглядят идентично, что может послужить основанием для подозрения, что две парадигмы мало отличаются друг от друга. Однако это не так. Достаточно сказать, что объекты во время исполнения программы могут менять свое значение, и это отражает их сущность как данных. Для процесса внутри парадигмы программирования, не ориентированного на объекты, самомодификация кода является плохим стилем программирования и применяется крайне редко.
Парадигма объектно-ориентированного программирования включает в себя три понятия:
Приведем словарь ключевых терминов объектно-ориентированного программирования, заимствованный из монографии [103].
Объект - компонент системы, представленный приватной (собственной) памятью и набором операций.
Сообщение - запрос объекту на выполнение одной из его операций.
Метод - описание того, как выполнять одну из операций объекта.
Класс - описание группы подобных объектов.
Экземпляр - один из объектов, описываемых классом.
Объект имеет возможность взаимодействия с внешними по отношению к нему объектами. В качестве такого средства взаимодействия в объектно-ориентированном программировании используется механизм посылки и приема сообщений, который, по-видимому, плохо соответствует представлению о взаимодействии систем, но лучшего ничего не придумали.
Итак, для того, чтобы заставить объект произвести какие-либо действия, т.е. начать выполнение программы, и получить от него результат, необходимо этому объекту послать сообщение-запрос. В ответ на запрос объект сам может послать последовательность сообщений другим объектам. В таком случае цепочка сообщений удлиняется, а трасса сообщений может ветвиться. Таким образом, организуется совместная деятельность объектов внутри вычислительной системы.
Последовательность сообщений, которые будут посланы одним объектом ряду других объектов, а может быть и самому себе (рекурсия), как реакция на получение сообщения и является моделью поведения объекта. Реакция объекта может быть различной в зависимости от вида полученного сообщения.
Приведем, согласно статье [11, с.16], общую характеристику парадигмы объектно-ориентированного программирования:
Приведенный анализ показывает, что парадигма объектно-ориентированного программирования имеет преимущества перед парадигмой традиционного программирования при формализации знаний, что объясняется сокращением так называемого семантического разрыва - разрыва между принципами моделирования реальных объектов и принципами, лежащими в основе языков программирования. Этот разрыв в работе [11, с.16] назван вторым семантическим разрывом, т.к. Г.Майерс [13] уже ввел понятие семантического разрыва между языками программирования и архитектурой ЭВМ.
Процедурность - специфическая часть наших знаний о мире, что согласуется с концепцией об особом назначении программного обеспечения формализации знаний. Поэтому, чтобы программное обеспечение было способно вбирать в себя знания, процедурность должна быть неотъемлемой частью языков программирования.
Но создается впечатление, что объектно-ориентированная парадигма автоматически исключает процедурность. Нет, этого не произошло. По этому поводу говорят, что процедуры инкапсулированы в объекте.
Теперь мы в состоянии дополнить "дерево парадигм", которое продолжает оставаться бинарным:
Рис.6. Дерево парадигм
Наконец, опишем третий уровень парадигм программирования, в котором находятся парадигмы программирования, названные нами метапарадигмами. Это последовательное, параллельное, конкурентное и распределенное программирование [14, с.120-122]. Данные направления выделены в соответствии со средствами взаимодействия процессов, способами организации процессов и целями их создания.
Процесс - последовательность предусмотренных событий, определяемая объектом или явлением и выполняющаяся в заданных условиях; течение событий, происходящих в соответствии с намеченной целью или результатом [7].
Последовательное программирование - это всем хорошо известное традиционное программирование. В нем на понятии процесса внимание, как правило, не акцентируется [14, с.120].
Параллельное программирование - совокупность языковых средств и методов решения задач на ЭВМ, допускающих параллельную обработку данных. Это могут быть мультипроцессорные системы с общей оперативной памятью, векторные процессоры, ассоциативные процессоры и т.д. Основная область применения параллельного программирования - решение сложных вычислительных задач. Основная цель - достигнуть максимального быстродействия компьютера. Характерной особенностью является то, что порождаемые процессы имеют, как правило, одну программу и действуют над данными одинаковой структуры [14, с.120-121].
Конкурентное программирование (термин "конкурентный" можно рассматривать как производный от слова "конкуренция") - программирование, при котором программа, решающая некоторую конкретную задачу, представляется как совокупность множества процессов, выполняемых параллельно по отношению друг к другу. Программы этих процессов обычно различны. Здесь понятие параллельности в большой степени условно, так как если эти процессы выполняются на одном компьютере, то в каждый момент времени действительно выполняется процессором только один из них. Иллюзия параллельности достигается за счет переключения процессора с одного процесса на другой в соответствии с некоторой дисциплиной обслуживания. Важно здесь то, что эти процессы выполняются на одной ЭВМ (возможно, на мультипроцессорной, но, как правило, на однопроцессорной) и в силу этого конкурируют между собой за ее физические и логические ресурсы: процессор, каналы ввода-вывода, области памяти, наборы данных и т.д. [14, с.121-122].
Отметим две основные вехи в развитии конкурентного программирования.
1978 год. Выходит в свет статья британского программиста и математика Чарльза Энтони Ричарда Хоара "Взаимодействующие последовательные процессы", заложившая математические основы конкурентного программирования.
1984 год. Фирма British Semiconductor Manufacturer INMOS разрабатывает язык Occam - "язык ассемблера" для вычислительных систем, построенных из множества параллельно работающих специальных микропроцессоров - транспьютеров. Язык назван в честь средневекового английского философа-схоласта и логика Уильяма Оккама (1285-1349) и основан на математической теории Ч.А.Р.Хоара. Основное понятие языка - процесс. Процессы могут выполняться как последовательно, так и параллельно и взаимодействуют с помощью каналов.
Распределенное программирование (Distributed Programming) - это совокупность языковых средств и методов программирования систем распределенной обработки данных в сетях ЭВМ и многомашинных комплексах. Можно сказать, что распределенное программирование - это конкурентное программирование без допущения наличия у процессов общей памяти [14, с.119].
Система распределенной обработки данных - система, отдельные компоненты которой одновременно функционируют на разных ЭВМ, обладающих средствами обмена данными друг с другом [14, с.119].
Вывод.
Парадигмы программирования имеют иерархическую структуру, представимую в виде дерева, изображенного на схемах ниже:
Рис.7. Структура парадигм программирования
Так, типичными языками конкурентного императивного программирования, не ориентированного на объекты, являются
Concurrent Pascal, Modula-2, Ada, Occam.
На следующем шаге мы познакомимся более подробно с парадигмой объектно-ориентированного программирования.