И НСТИТУТ А КТУАЛЬНОГО О БРАЗОВАНИЯ “Ю Р И НФО Р-МГУ” Серия: Компьютерные науки и информационные технологии
В.Э. Вольфенгаген
КОНСТРУКЦИИ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ Приемы описания
Москва Учебно-консультационный центр “ЮрИнфоР” 2001
II
Серия: Компьютерные науки и информационные технологии Кафедра перспективных компьютерных исследований и информационных технологий Автор: д.т.н., профессор Вольфенгаген В.Э. Конструкции языков программирования. Приемы описания.М.: Учебно-консультационный центр “ЮрИнфоР”, 2001.– 322 с. ISBN В работе изложены основы, касающихся разработки, реализации и применения конструкций как императивных, так и функциональных языков программирования. Значительное внимание уделяется применению денотационной семантики, позволяющей в полной мере извлечь преимущества объектно-ориентированного подхода, что, в конечном счете, позволяет построить результирующую вычислительную модель чисто функционального типа. Изложение материала сопровождается детально разобранными которые снабжены комментариями, помогающими уяснить реализацию конструкций различных языков. Книгу можно использовать в качестве учебника или справочного пособия. Она будет полезна как студентам и аспирантам, так и профессионалам в области компьютерных наук, информационных технологий и программирования. c
В.Э. Вольфенгаген, 1997-2001 c
Учебно-консультационный центр “ЮрИнфоР”, 1997-2001 Учебно-консультационный центр “ЮрИнфоР” Институт Актуального Образования ЮрИнфоР-МГУ Fax: +7 (095) 299-8415 E-mail:
[email protected] Издание осуществлено при поддержке РФФИ, проект 01-01-14068
Оглавление Предисловие редактора серии
1
Предисловие
3
Введение
13
1 Модель вычислений 1.1 Возможности модели вычислений . . . . . . . . . . 1.2 Синтаксис, семантика и прагматика . . . . . . . . . 1.3 Формальная семантика и ее возможности . . . . . . 1.4 Денотационная семантика . . . . . . . . . . . . . . . 1.5 Абстрактные объекты . . . . . . . . . . . . . . . . . 1.6 Технологическая основа . . . . . . . . . . . . . . . . 1.7 Унификация логики программ и систем баз данных
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
17 18 20 20 23 25 27 28
2 Потоковые диаграммы 2.1 Основные идеи теории вычислений . . . . . . . . . 2.2 Выражения, представляющие элементы . . . . . . 2.2.1 Тождественная функция . . . . . . . . . . 2.2.2 Константная функция . . . . . . . . . . . . 2.2.3 Произведение . . . . . . . . . . . . . . . . 2.2.4 Сумма . . . . . . . . . . . . . . . . . . . . 2.3 Представление диаграммами . . . . . . . . . . . . 2.3.1 Выражения, представляющие диаграммы . 2.3.2 Универсум для рассуждения о диаграммах 2.4 Основные математические понятия и определения
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
31 32 33 34 35 35 37 38 42 43 44
III
. . . . . . . . . .
IV
Оглавление 2.5
Идея потоковых диаграмм . . . . . . . . . . . . . . 2.5.1 Построение набора примитивных сущностей 2.6 Абстрактная потоковая машина . . . . . . . . . . . 2.6.1 Значение выражений . . . . . . . . . . . . . 2.6.2 Оценивающее отображение . . . . . . . . . 2.6.3 Означивание while-цикла . . . . . . . . . . . 2.7 Циклические диаграммы . . . . . . . . . . . . . . . 2.8 Синтаксический анализ E . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
47 48 50 50 53 54 56 64
3 Пример непосредственной семантики для модели вычислений 3.1 Неформальный синтаксис . . . . . . . . . . . . . . . . . . . 3.2 Неформальная семантика . . . . . . . . . . . . . . . . . . . 3.2.1 Неформальная семантика выражений . . . . . . . . 3.2.2 Неформальная семантика команд . . . . . . . . . . 3.3 Формальная семантика . . . . . . . . . . . . . . . . . . . . 3.3.1 Синтаксис . . . . . . . . . . . . . . . . . . . . . . . 3.3.2 Состояние, память, вход, выход и значение . . . . . 3.3.3 Семантические функции . . . . . . . . . . . . . . . 3.3.4 Семантические предложения . . . . . . . . . . . . . 3.3.5 Особенности формальной семантики . . . . . . . . 4 Исходные понятия и обозначения: теория вычислений 4.1 Абстрактный синтаксис . . . . . . . . . . . . . . . . . 4.2 Подходы к построению семантики . . . . . . . . . . . 4.3 Домены . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Рекурсивно определенные функции . . . . . . 4.3.2 Рекурсивно определенные множества . . . . . 4.3.3 Теория вычислений . . . . . . . . . . . . . . . 4.3.4 Математизация в программировании . . . . . 4.4 Определение доменов . . . . . . . . . . . . . . . . . . 4.4.1 Стандартные домены . . . . . . . . . . . . . . 4.4.2 Конечные домены . . . . . . . . . . . . . . . . 4.4.3 Конструкторы доменов . . . . . . . . . . . . . 4.4.4 Равенства среди доменов . . . . . . . . . . . . 4.5 Функции . . . . . . . . . . . . . . . . . . . . . . . . . 4.5.1 Ламбда-обозначения . . . . . . . . . . . . . . 4.5.2 Функции высших порядков . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
67 69 69 70 72 72 72 74 74 77 80
81 82 84 87 87 88 89 90 90 90 91 91 95 96 97 102
V
Оглавление 4.5.3 4.5.4 4.5.5 4.5.6 4.5.7 4.5.8 4.5.9 4.5.10 4.5.11 4.5.12
Соглашения об опускании скобок . . . . . . . . . . Каррирование . . . . . . . . . . . . . . . . . . . . . Условные функции . . . . . . . . . . . . . . . . . . . Case-конструкция . . . . . . . . . . . . . . . . . . . Функции коррекции . . . . . . . . . . . . . . . . . . Порождающие функции . . . . . . . . . . . . . . . . Способы определения функций, включая рекурсию Исключение переменных . . . . . . . . . . . . . . . Where-конструкция . . . . . . . . . . . . . . . . . . Композиция и построение последовательности . . .
103 104 106 107 108 109 110 111 113 114
5 Пример денотационного описания для модели вычислений 119 5.1 Абстрактный синтаксис . . . . . . . . . . . . . . . . . . . . 120 5.1.1 Синтаксические домены . . . . . . . . . . . . . . . 121 5.1.2 Синтаксические предложения . . . . . . . . . . . . 121 5.2 Семантика . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 5.2.1 Семантические домены . . . . . . . . . . . . . . . . 121 5.2.2 Вспомогательные функции . . . . . . . . . . . . . . 121 5.2.3 Семантические функции . . . . . . . . . . . . . . . 122 5.2.4 Семантические предложения . . . . . . . . . . . . . 123 6 Стандартная семантика 6.1 Продолжения . . . . . . . . . . . . . . . . . . . . . . 6.1.1 Представление ‘остатка программы’ . . . . . 6.1.2 Продолжения команд . . . . . . . . . . . . . 6.1.3 Продолжение выражения . . . . . . . . . . . 6.1.4 Непосредственная семантика и семантика должений . . . . . . . . . . . . . . . . . . . . 6.2 Размещение, запас и среда . . . . . . . . . . . . . . 6.2.1 Разделение . . . . . . . . . . . . . . . . . . . 6.2.2 Переменная и размещение . . . . . . . . . . 6.2.3 Запас . . . . . . . . . . . . . . . . . . . . . . 6.2.4 Среда . . . . . . . . . . . . . . . . . . . . . . 6.3 Стандартные домены значений . . . . . . . . . . . . 6.4 Блоки, декларации и диапазоны . . . . . . . . . . . 6.5 Стандартные домены продолжений . . . . . . . . . 6.5.1 Продолжение команды . . . . . . . . . . . . 6.5.2 Продолжение выражения . . . . . . . . . . .
. . . . . . . . . . . . про. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
125 . 127 . 127 . 128 . 129 . . . . . . . . . . .
129 130 130 131 131 132 133 133 135 135 135
VI
Оглавление 6.5.3 Продолжение декларации . . 6.6 Стандартные семантические функции 6.7 Преобразование продолжений . . . . 6.8 Присваивание и значения L, R . . . . 6.9 Процедуры и функции . . . . . . . . . 6.9.1 Процедуры . . . . . . . . . . . 6.9.2 Функции . . . . . . . . . . . . 6.9.3 Свод основных соотношений .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
136 136 139 141 144 144 146 147
7 Пример денотационной семантики 7.1 Пример синтаксиса . . . . . . . . . . 7.1.1 Синтаксические домены . . . 7.1.2 Синтаксические предложения 7.2 Пример семантики . . . . . . . . . . . 7.2.1 Семантические домены . . . . 7.2.2 Семантические функции . . . 7.2.3 Семантические предложения . 7.3 Пример означивания . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
149 150 150 151 151 151 152 153 157
. . . . . . .
161 162 162 163 164 166 167 170
8 Выходы и передачи управления 8.1 Выходы . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.1.1 Выходы из команд . . . . . . . . . . . . . . . . . . 8.1.2 Выходы из выражений . . . . . . . . . . . . . . . 8.1.3 valof и resultis . . . . . . . . . . . . . . . . . . . . 8.2 Переходы . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.1 Семантика переходов . . . . . . . . . . . . . . . . 8.2.2 Дополнительные аспекты семантики переходов . 8.2.3 Присваивание переменным меток в качестве значений . . . . . . . . . . . . . . . . . . . . . . . . . 9 Разновидности процедур и функций 9.1 Число параметров . . . . . . . . . . . . . . . . . . . . . . 9.1.1 Процедуры и функции без параметров . . . . . . . 9.1.2 Процедуры и функции более, чем с одним параметром . . . . . . . . . . . . . . . . . . . . . . . . 9.2 Рекурсивные процедуры и функции . . . . . . . . . . . . 9.3 Статическое и динамическое связывание . . . . . . . . . 9.3.1 Семантика связывания . . . . . . . . . . . . . . .
. 172 175 . 177 . 177 . . . .
178 179 181 181
VII
Оглавление
9.4
9.5
9.6 9.7 9.8
9.3.2 Особенности динамического связывания . . . . . . 182 Механизмы передачи параметров . . . . . . . . . . . . . . 183 9.4.1 Вызов по значению . . . . . . . . . . . . . . . . . . 183 9.4.2 Вызов по ссылке . . . . . . . . . . . . . . . . . . . . 185 9.4.3 Вызов по значению и результату . . . . . . . . . . . 186 Механизмы вызова процедур . . . . . . . . . . . . . . . . . 188 9.5.1 Вызов по замыканию . . . . . . . . . . . . . . . . . 189 9.5.2 Вызов по тексту . . . . . . . . . . . . . . . . . . . . 190 9.5.3 Вызов по обозначению . . . . . . . . . . . . . . . . 191 9.5.4 Конструкции цитирования . . . . . . . . . . . . . . 192 Краткий обзор механизмов вызова и передачи . . . . . . . 192 Абстракции: выражения, обозначающие процедуры и функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Механизмы связывания деклараций . . . . . . . . . . . . . 195
10 Структуры данных 10.1 Операции над множествами . . . . . . . . . . . . . . . 10.2 Ссылки . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 Массивы . . . . . . . . . . . . . . . . . . . . . . . . . . 10.4 Записи . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.5 Выражения со структурой данных в качестве значения 10.6 Файлы . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.7 Семантика файлов . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
197 198 199 200 203 204 206 207
11 Конструкции итераций 11.1 repeat C until E . . . . . . . . 11.2 Циклы event . . . . . . . . . 11.3 For-предложения . . . . . . 11.4 Обобщения for-предложения
. . . .
. . . .
211 211 212 212 216
12 Типы 12.1 Разновидности типов . . . . . . . . . . . . . . . . . . . . 12.2 Правильно типизированные программы и проверка типов 12.2.1 Проверка типов . . . . . . . . . . . . . . . . . . . 12.2.2 Схема денотационного описания проверки типов . 12.3 Семантика типов . . . . . . . . . . . . . . . . . . . . . . .
. . . . .
219 220 221 221 222 223
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
VIII
Оглавление
13 Аспекты функционального программирования 13.1 Проблематика . . . . . . . . . . . . . . . . . . . . . 13.2 Основные характеристики функциональных языков 13.3 Типичные функциональные языки . . . . . . . . . . 13.3.1 Meta Language . . . . . . . . . . . . . . . . . 13.4 Основные методы реализации . . . . . . . . . . . . 13.4.1 SECD-машина . . . . . . . . . . . . . . . . 13.4.2 Редукция графа . . . . . . . . . . . . . . . . 13.4.3 Категориальная абстрактная машина . . . . 13.5 Особенности программирования . . . . . . . . . . . 13.5.1 Простота семантики . . . . . . . . . . . . . . 13.5.2 Проверка корректности программы . . . . . 13.5.3 Преобразование программы . . . . . . . . . 13.6 Синтаксис упрощенного функционального языка . . 13.6.1 Функциональный язык первого порядка . . . 13.6.2 Функциональный язык высших порядков . . 13.7 Получение из λ-выражений машинных инструкций 13.7.1 Компилирование правила вычисления . . . . 13.7.2 Компилирование управления средой . . . . . 13.7.3 Большой пример . . . . . . . . . . . . . . . . 13.8 Ленивые и жадные вычисления . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
225 227 228 237 237 241 241 242 243 244 244 247 249 249 250 250 251 253 261 271 284
Литература
287
Предметный указатель
297
Глоссарий
305
Предисловие редактора серии Компьютерные науки (computer science) и информационные технологии стали вездесущими и продолжают сулить перемены, которые еще больше затрагивают практически все сферы нашей жизни. Прежде всего новые технологии расширяют доступ к разнообразной информации и продуцируют массу информации, представленной в электронной форме, изменяя как характер труда, так и его результаты. Действительно, многие продукты, пользующиеся массовым спросом, выпускаются в виде последовательности битов, а темп их изменений необычайно высок. Изменяются как отдельные профессии, так и целые отрасли индустрии или знания. По-сути, развитие информационных технологий дало виртуальную реальность, к которой современное общество только начинает адаптироваться и возможности которой достаточно быстро осваиваются. Если на ранних стадиях программирование представляло собой вид искусства, когда программист писал программу для решения определенной задачи и сопровождал ее более или менее подробно составленной документацией, то теперь создана мощная индустрия программирования с сопутствующей ей инженерией программирования. В настоящее время исследованиях по программированию или в сфере компьютерных наук, как правило, поддерживаются работы, в которых вносится некоторое небольшое улучшение в решение уже хорошо известной проблемы. Вместе с тем из виду упускаются действительно важные и фундаментальные исследования, ведущие к поиску новых концепций вычислений на компьютере и недостаточное внимание уделяется накоплению знаний 1
Предисловие редактора серии в области программирования. Публикации в этой серии задуманы как продолжающийся ряд работ в области компьютерных наук, информационных технологий и программирования, способствующих накоплению знаний в этих областях. Предполагается, что публикуемые работы могут быть использованы для изложения отдельных курсов и вместе с тем способствовать научным исследованиям. Как правило, книги серии предназначаются для удовлетворения читателей самого различного уровня – от студентов, ставящих перед собой целью первичное ознакомление с самим предметом, до специалистов в разделах компьютерных наук. Таким образом в публикациях серии предполагается отражать текущее состояние дел в данной области и дать основу для систематического изучения разделов компьютерных наук, информационных технологий и программирования. Редакторы серии
2
Предисловие Тот, кто вступает в разговор о языках программирования, обычно предполагает удовлетворить определенные ожидания. На практике ожиданий оказывается целый спектр – в полной зависимости от профессиональных интересов пользователя. Прагматически настроенный человек хотел бы усвоить определенный круг идей, пользуясь которыми можно продвигаться в освоении конструкций конкретно выбранного – для вполне определенных целей, – языка программирования. Действительно, на сегодня еще недавно популярная тема об универсальных языках программирования отошла на задний план. Пользователю предлагается большой набор языков и обширных руководств к каждому из них, настолько обширных, что изучение хотя бы одного из них требует значительных усилий. Типичное руководство по языку программирования выглядит наподобие ‘руководства пользователя’ и оказывается построенным рецептурным способом по принципу: делайте именно так, тогда получите определенный желательный эффект, причем никогда не делайте некоторых вещей, поскольку получится нежелательный эффект. Во многих случаях это действительно хорошая манера изложения, которая помогает сравнительно быстро начать что-то практически записывать, компилировать и исполнять. Однако в такого рода руководствах по языкам программирования полностью обходится вопрос, ради чего строится язык как система программирования и что же лежит в его основе. Между тем без чрезмерных усилий можно выделить некоторую центральную сущность, которая подразумевается в каждом без исключения языке программирования, и этой сущностью оказывается процесс в математическом смысле этого слова, называемый ‘вычислением’. Вычисление как таковое может быть 3
Предисловие вычленено и рассмотрено с точки зрения описывающих его конструкций конкретного языка. Более того, вычисление как самостоятельный объект исследования давно подвергается массированным математическим “натискам”. Повидимому, каждое очередное поколение математиков от computer science пытается по-своему переосмыслить и переоткрыть основные законы вычислений. Самое существенное, что содержание понятия вычисления не остается чем-то неизменным, но периодическим меняется. Тем самым вновь и вновь возникает необходимость выработки надлежащих теоретических оснований, приемов и методов формализации, которые хотя бы на время удовлетворяли новым требованиям.
Цель книги ´ Одна из основных идей книги – показать важность развития видения программы в терминах объектов или функций. Даже если синтаксически это не очевидно, и программист имеет дело с конструкциями выражений и команд, то семантика может быть построена на принципах исчислений объектов. В этой связи описание конструкций языков программирования средствами денотационной семантики характеризует именно объекты и их взаимосвязи, а ее возможности сравнительно недавно осмыслены и только начали себя проявлять на практике. Извлекаемый практический результат состоит в развитии навыка составления модели вычислений, кратко и вместе с тем исчерпывающе описывающей все существенные черты разрабатываемого программного обеспечения.
Кому адресована книга Эта книга написана в помощь тем программистам, которые хотят привести свои знания в систему и переосмыслить тот круг идей, с которым приходится сталкиваться на практике. Книга призвана оказать помощь в чтении и изучении оригинальных работ в области компьютерных наук, информационных технологий и программирования, а также в случае необходимости произвести проработку вновь создаваемых механизмов программирования. 4
Предисловие Книгу можно использовать в качестве учебника или справочного пособия. Она будет полезна как студентам и аспирантам, так и профессионалам в этих областях.
Рассуждения в терминах объектов К настоящему времени в области программирования предлагается удивительно много формализаций, дающих приемы описания программистских конструкций, и каждая из них является совершенно своеобразным языком общения сравнительно ограниченного круга специалистов, которые хорошо понимают друг друга. Однако попытка “непосвященного” понять практическую пользу и значимость нового математического языка наталкивается на препятствия. Прежде всего на уже известные трудности важно суметь взглянуть под новым углом зрения. Так распространение объектно-ориентированного программирования требует и привлечения других способов рассуждения, которые зачастую радикально отличаются от стереотипов рассуждения в процедурном программировании. Точно также лишь немногие и сравнительно молодые математические теории ориентированы на рассуждения в терминах объектов, а не в терминах операторов, как это следует из опыта изучения математического анализа в большинстве университетов, в том числе, и технического или компьютерного профиля. К сожалению, программисту весьма редко удается прослушать университетский курс, закладывающий основы математического мышления в терминах объектов. В лучшем случае дело ограничивается сообщением чисто математических результатов, которые не так-то просто преломить на практическое программирование без известной теоретической искушенности.
Значение моделей вычислений Программирование в терминах объектов требует создания и поддержания собственной математической культуры, дающей весь спектр стимулирующих идей. Программист при решении вполне конкретной задачи становится исследователем, от которого требуется создание собственного языка со своими возможностями. Эти возможности не всегда интуитивно очевидны, и могут потребоваться чисто математические оценки 5
Предисловие их выразительных возможностей. Кроме того, часто требуется не просто написать некоторый программный код, но и выполнить его оптимизацию, не теряя свойства эквивалентности исходному коду. Все это требуется для аккуратного и профессионального проведения разработки своей собственной “математической оболочки”, в которой поддерживаются все значимые и интересные математические приложения.
Значимость программирования Одну из самых больших опасностей для успешных исследований не только в области программирования, но и в computer science в целом связана с чрезмерной распространенностью и разнообразием языков программирования, а также с их чрезмерной практической значимостью. Современное общество увлечено компьютеризацией во всех мыслимых областях деятельности. Computer science и информационные технологии находятся в центре внимания, особенно те их аспекты, которые могут найти непосредственное коммерческое применение, и в первую очередь сюда относятся языки программирования. Такое внимание имеет и оборотную сторону, поскольку зачастую оно может идти во вред фундаментальности, глубине и качеству исследования. Другой аспект состоит в том, что по мере нарастания интенсивности исследовательских работ и применения их результатов растет и стремление сохранить их результате в секрете. Эта тенденция наблюдается даже в академической среде, хотя именно здесь главным критерием научной продуктивности является опубликование результатов. В целом давление коммерции возрастает, отвлекая внимание исследователей от поиска принципиально новых путей и подходов к эксплуатации текущих идей по принципу небольшого приращения и улучшения – ‘дельта X’. Вместе с тем для программирования характерно – в отличие от техники, – значительное сокращение пути от фундаментального исследования к перспективной разработке и применению. Дело еще и в том, что языки программирования – наиболее продвинутый в теоретическом отношении раздел computer science. Формализация является неотъемлемой частью любого продвижения в области программирования. Математический аппарат здесь использовался, начиная с самых первых шагов, с появления самых первых языков программирования. Именно в этой области традиционная математика испытывает значительные трудности, стимулируя рост исследований 6
Предисловие в области метаматематики, касающихся самой природы формализации. Вот почему тому, кто работает в области программирования либо просто время от времени пишет программы, приходится владеть новейшими методами формализации и постоянно пополнять свои знания.
О чем эта книга По своей структуре книга состоит из трех частей. В разделах 1–2 излагаются начальные сведения о модели вычислений и приводится модель вычислений в форме потоковых диаграмм. В разделах 3–12 на примерах приводится замкнутое изложение особенностей различных конструкций языков. В разделе 13 изложены приемы описания конструкций языков функционального типа. В разделе 1 Модель вычислений содержатся самые начальные понятия, необходимые для понимания всех дальнейших конструкций, которые вовлекаются в проект в области программирования. В нем устанавливается основа для видения основных целей и задач, связанных с успешных выполнением проекта. Здесь устанавливаются структурные начала для организации и успешного выполнения работ. В разделе 2 Потоковые диаграммы представлена идеализация, которая охватывает основные ‘строительные блоки’. Из них конструируется ‘среда’, в условиях которой происходит ‘протекание информации’. Эти строительные блоки, как окажется, распадаются на два класса. Один из них, как будет ясно, можно графически изобразить диаграммами, то есть элементами, аналогичными тем, из которых строится структурная схема программы, то есть потоковая диаграмма, а другие таким способом представить не удается. С другой стороны вместо графического языка потоковых диаграмм можно использовать математический язык – язык полных решеток, средствами которого элементы могут различаться и далее. В конечном счете целью является построение модели программы или даже модели вычислений, происходящих в программе, – с точки зрения протекающей в программе информации. Следовательно, целью настоящего раздела является описание модели, являющейся теорией вычислений и проявляющей полезные математические свойства. В разделе 3 Пример непосредственной семантики для модели вычислений приводится пример построения непосредственной семантики, который носит чисто иллюстративный характер. Для этого отобра7
Предисловие ны наиболее часто используемые конструкции, реализующие условное выражение, один из вариантов цикла и т.п. В целом сохранено соответствие схеме построения М. Гордона. Раздел 4 Исходные понятия и обозначения: теория вычислений содержит справочный материал, касающийся необходимого и минимального запаса математических понятий, которым следует владеть программисту. Основной круг вопросов следующий. Во-первых, приводится элементарное изложение построения абстрактного синтаксиса. Во-вторых, разъясняется необходимость использования понятия доменов вместо множеств, что соответствует духу теории вычислений. Втретьих, рассматриваются различные варианты доменов и способы их построения. В-четвертых, обсуждается понятие функции, необходимое программисту, особенно в связи с проектированием языка программирования. Раздел 5 Пример денотационного описания для модели вычислений содержит конспективное изложение семантики примера языка, в ходе которого вводятся вспомогательными функциями. Использование этих математических средств дает возможность построения весьма компактной семантики, причем в то же самое время сохраняется высокая степень читаемости семантических предложений. Раздел 6 Стандартная семантика охватывает средства разработки семантики реальных языков, когда она приобретает специальный вид, в рамках которого учитываются все возникающие вычислительные эффекты. В этом случае упрощенные математические идеализации подвергаются усовершенствованиям. (i) Использование понятия ‘состояние’ принимает форму непрямого использования (в отличие от базовых математических моделей, используемых для построения семантик). При этом вводится ‘продолжение’, которое позволяет обрабатывать операции переходов. Напомним, что эти операции ‘переключают’ протекание информации в программе, нарушая ‘естественный ход вычислений’. (ii) Связывание идентификаторов с их значениями становится двухэтапным. Сначала идентификаторы отображаются на ‘переменные’, затем ‘переменные’ отображаются на значения. Такой прием позволяет ввести разделение, или псевдонимию. Предыдущие ограничения, накладываемые на семантику, принимают8
Предисловие ся многими исследователями. Семантику, содержащую продолжения и процедуру двухэтапного связывания идентификаторов, называют стандартной семантикой. Преимуществом стандартизации семантики является облегчение сравнения возможностей различных языков. Вместе с тем ограничение технических приемов условиями стандартизации может затруднить представление конкретного языка программирования. В частности, ‘динамическое связывание’, применяемое в LISP, напрямую не укладывается в двухэтапную схему связывания. В разделе 7 Пример денотационной семантики приведен пример построения денотационной семантики. Язык также играет чисто иллюстративную роль. На примере его конструкций показано, как разрабатывается стандартная семантика (с продолжениями). Приводимый пример языка может быть полезен в качестве некоторого языка-ядра, которое по мере необходимости можно расширять. В разделе 8 Выходы и передачи управления рассмотрен вопрос представления различных конструкций, которые изменяют ‘протекание информации’, или ‘протекание управления’. С точки зрения семантики происходит игнорирование нормального продолжения, а управление передается другому продолжению. Конструкции, обеспечивающие осуществление этого эффекта, называются секвенсерами (делителями). Раздел 9 Разновидности процедур и функций содержит обсуждение различных вариантов использования процедур и функций. Рассматриваемые случаи сводятся к следующему: (1) различное число параметров, (2) возможность рекурсии, (3) связывание идентификаторов в среде в случае, когда идентификаторы из тела не являются формальными параметрами, (4) ограничения, накладываемые на значение фактических параметров, (5) означивание выражений, задающих фактические параметры. В разделе 10 Структуры данных представлено обсуждение возможности установления и использования четырех видов структур данных: ссылок (указателей), массивов, записей и файлов. У ссылок имеется единственная компонента. Массивы и записи имеют несколько компонент, причем их число устанавливается при объявлении соответствующей структуры. У файлов динамически изменяемое число компонент. Доступ к компонентам массивов выполняется по индексу, например, A[3]. Доступ к компонентам записей производится квалификацией имен, например, R.three. Доступ к компонентам файлов организуется путем перемещения ‘окна’ вдоль этого файла. 9
Предисловие В разделе 11 Конструкции итераций рассмотрены конструкции итерирования вычислений repeat C until E, loop C then I1 : C1 ; . . . ; In : Cn end вместе с event I, а также конструкцию f or I := F do C. Раздел 12 Типы содержит обсуждение средств типизации. Явная типизация преследует ряд целей: облегчается читаемость программ, улучшается возможности их отладки, в частности 1 + true фиксируется как ошибка в типе до выполнения программы, повышается эффективность скомпилированного кода, поскольку во время исполнения программы не нужно тратить усилия на проверку типов. В разделе 13 Аспекты функционального программирования посвящен специальному подходу к программированию, который, как считается, наиболее близко соответствует теории. Термин ‘функциональная программа’ понимается набор определений функций, которые по мере необходимости вычисляются компьютером. Для решения поставленной задачи программисту приходится создавать такую функцию, которая как раз и обеспечивает это решение. Таким образом обеспечивается правило, связывающее вход и выход программы. Эта функция может возникнуть как комбинация других более простых или даже примитивных функций, в том числе определенных самим пользователем.
Как пользоваться книгой Настоящая книга содержит тринадцать разделов, в которых последовательно излагается типичный проект построения некоторого небольшого языка программирования примерно в той последовательности, в которой это выполняется в реальном проекте. Предполагается, что любому из читателей следует просмотреть первые три раздела книги для овладения основами проектирования языка программирования. Некоторые из читателей могут предпочесть изучить книгу от начала до конца, однако нет необходимости придерживаться какого-либо предопределенного порядка, а использовать ее для справки по мере необходимости включения в проект тех или иных вычислительных конструкций.
Рекомендации по обучению Если книга используется в академических целях для изложения или изучения данного предмета, то наиболее эффективно ее можно использовать в курсах компьютерных наук, информационных технологий и про10
Предисловие граммирования. В таком курсе акцентируется аспект означивания используемой конструкции, который наиболее близко соответствует реальной практике достижения целей проекта языка программирования. В курсах компьютерных наук и программирования книга может изучаться в последовательности от начала до конца. В курсах же информационных технологий разделы 1, 3, 4 и 13 представляют наибольший интерес. В случае достаточного уровня подготовки занимающихся, они могут дополнительно изучить разделы 6, 9 и 10. Во многих учебных заведениях проводятся занятия со слушателями, специализирующимися в области информационных технологий. В этом случае книга вполне подходит для организации занятий и может быть изучена от начала до конца.
Ошибки Книга такого рода не может быть совершенно свободна от ошибок. В случае обнаружения ошибки или в случае, когда у вас есть предложения по ее исправлению автор будет рад узнать об этом. В случае, если у вас появятся новые задачи и упражнения, автор будет особенно рад узнать об этом, но если вы захотите их прислать, то, пожалуйста, присылайте их с решениями, воспользовавшись адресом электронной почты:
[email protected].
Благодарности Искренняя признательность выражается А.С. Кузичеву, в обратившему мое внимание на важность изучения и применения исчислений объектов, а также об-систем и комбинаторно полных систем. Участие в работе руководимых им спецкурсов по неклассическим логикам оказало стимулирующее влияние. На начальных этапах, когда определялся круг излагаемых методов, неоценимую помощь оказало плодотворное сотрудничество с А.Ф. Саевским, которому автор искренне признателен. Совместно с ним проработан ряд вопросов, относящихся к вычислениям на абстрактных машинах. Глубокая благодарность выражается А.Г. Пантелееву, предоставившему реализованный им Lisp-интерпретатор, и разъяснившему множество деталей, касающихся реализации объектных и функциональных языков, а также путей и методов их эффективного использования. Его 11
Предисловие поразительная глубина проникновения в методы реализации операций связывания переменных, структур данных, процедур, функций и механизмов рекурсии стимулировала всех тех, кто с ним соприкасался в области программирования. Его глубокая интуиция профессионального системного программиста не раз помогала найти верные решения и построить эффективную реализацию программы. Стимулирующее влияние на раннем этапе работы над материалом книги оказал Э.М. Галстян. Трудно переоценить его организационные усилия, благодаря которых стала возможной работа научного семинара по исчислениям высших порядков и символьным вычислениям – в области исчислений функций и их приложений к computer science и программированию. Автор признателен всем, кто принимал участие в подготовке этой книги. В первую очередь это относится к читателям первого препринтного издания сокращенного варианта, которые поделились своими замечаниями, а также сообщили об ошибках и удачных местах. Множество таких предложений отражено в книге, а слушатели курса по языкам программирования С. Брызгалов, Е. Пивоварова, С. Зыков, Ю. Парфенов проделали большую работу по подготовке и проверке целого ряда примеров из раздела 13. Специальная благодарность А. Лурье, Е. Патрикеевой, Д. Николаенко, О. Макарову, проделавшим громадную работу по разбору решений большого числа примеров, задач и упражнений и внесших ряд ценных предложений. Все эти пожелания учтены в настоящей работе. Я хотел бы выразить благодарность всем коллегам, сотрудникам, участникам научных семинаров, слушателям учебных курсов и студентам, которые внесли целый ряд предложений по улучшению изложения отдельных вопросов, связанных с абстрактными машинами. Я хотел бы поблагодарить редакторов серии “Компьютерные науки и информационные технологии” за то внимание, которое было моей работе, а также моих коллег из Института Актуального Образования “ЮрИнфоР-МГУ”, поддерживавших меня на всех этапах работы над настоящим изданием. г. Москва август 2001 года
В.Э. Вольфенгаген
12
Введение Подходов и стилей программирования много, и это отражает картину совершенствования и распространения все новых и новых компьютерных архитектур. Возникающие архитектуры ориентированы на новые подходы к программированию, которые еще только зарождаются в исследовательских лабораториях. Новейшие парадигмы программирования в сильной степени выросли из математического способа рассуждений, принятого в теории вычислений. В частности, одной из ее начальных посылок была концепция “протекания информации” вдоль некоторого “возможного” русла, что привело к возникновению весьма плодотворной концепции программы, управляемой потоком данных. Другой пример связан с идеей использования некоторой части комбинаторной логики, построив в ней специальные объекты-инструкции. Эти объекты образуют систему команд абстрактной машины, которая может быть с успехом положена в основу вполне практических (но объектно-ориентированных) систем программирования. Возможность применения круга идей логических теорий и их переноса на область программирования позволили существенно улучшить концептуальное понимание языков программирования и особенностей применения их конструкций. Вместе с тем хорошо известна сложность выработки точного представления о языке и его вычислительных возможностях. Опыт работ в этой области показывает, что всякое продвижение на этом пути оказывается равнозначным технологическому прорыву или даже технологической революции – в первую очередь связанной с установлением нового понимания роли и места компьютера в высоко технологичном (и компьютеризованном) обществе. Однако, как и следовало ожидать, проблема семантики вычислений осталась и 13
Введение похоже сохранилась не только в исходном объеме, но и расширившись на область открытых систем и WIS (Web Information Systems). Имеется множество примеров, каким образом описывается вычисление конструкций языков программирования, однако все еще не получены ответы на целый ряд вопросов, среди которых встречаются следующие: что такое вычислительная машина? что такое ‘процесс вычисления’? насколько хорошо вычислительная машина моделирует процесс вычисления? Как известно, программы входят в описание процессов вычисления. Успехи теории автоматов смогли по большей части прояснить алгебраические аспекты вычислений и по преимуществу с конечным числом состояний. Понимание свойств действительно сложных программ, например, реализующих распределенные вычисления или работающих в условиях открытых информационных систем, требует существенного исследования свойств бесконечных объектов, причем существенно предполагает использование нескольких уровней объяснений – от изначальной предметной идеи вплоть до поддерживающей абстрактной машины и окончательного моделирования на компьютере конкретного типа в условиях конкретно выбранной системы программирования. К настоящему времени преобладающее распространение получили неформальные приемы описания языков программирования. В этом случае книга, излагающая такое описание, предполагается потенциально доступной максимально широкому кругу читателей. К сожалению, эта потенциальная широта далеко не всегда реализуется, поскольку, например, достаточно искушенные читатели будут предпочитать более краткие и концептуальные руководства, с меньшими неоднозначностями излагающие наиболее существенные идеи и просто показывающие, в каком же направлении следует работать дальше. В свою очередь, формальные описания языков программирования время от времени возникают в исследовательских статьях, однако понастоящему широкого распространения они не получили. Одно из объяснений заключается в отсутствии завершенного и вместе с тем интуитивно ясного метода описания. Зачастую имеющиеся формализации отличаются чрезмерной сложностью, изобилуют запутанными деталями, и для своего овладения требуют поистине неординарных усилий. Вместе с тем надежные и удобные формализации необходимы и важны для разработки, развития и стандартизации языков программирования. При выработке описания языков программирования возникло несколько тенденций. В соответствии с конструктивной точкой зрения 14
Введение язык определяется тем действием, которое он оказывает на компьютер. Поскольку непосредственное следование этой концепции приводит к высокой сложности процедур трансляции (перевода), то в качестве компромисса место реального компьютера занимает его математическое представление – идеализация или вычислительная модель, – называемое ‘абстрактной машиной’. Предполагается, что такая машина хорошо отражает основные возможности реального компьютера, однако имеет технические преимущества простоты и интуитивной ясности описания и представления вычислительного процесса. Среди сторонников этого подхода немало исследователей, усилиями которых сформированы современные представления о языках программирования, таких как Маккарти (J. McCarthy, [55]), Лендин (P. Landin, [50]– [51]), Стрейчи (C. Strachey, [87]) и целый ряд других. Так, исходя из подобных представлений, А.Г. Пантелеевым был построен эффективный Lisp-интерпретатор [3], [1]. При неконструктивном подходе формулируются утверждения об общих свойствах программ, из которых потенциально можно вывести утверждение об определенных свойствах некоторой конкретно написанной программы. В частности, такую точку зрения отстаивал Хоор (C.A.R. Hoare, [95] и некоторые другие. На практике, как и следовало ожидать, большинство предложений по формальному описанию языков программирования включает как конструктивные, так и неконструктиные аспекты. Обычно их применение проявляет себя с лучшей стороны на специально подобранных примерах небольших языков, в то время как для больших языков все начинает выглядеть значительно запутаннее. Языки программирования обладают хорошо развитой синтаксической формализацией и обычно используются как инструмент выяснения, являются ли те или иные вопросы логического характера принципиально вычислимыми. Вместе с тем построение семантической теории языков все еще далеко от завершения. Обычная точка зрения состоит в том, чтобы пользуясь конструкциями языка программирования сформировать желаемый ‘поток вычислений’. Периодически для разработки той или иной проблемы возникает необходимость “переключать” вычисления. Это не означает направить их в некоторое другое русло. На самом деле предполагается, что то же самое вычисление продолжается, однако модифицируется среда его выполнения. 15
Введение Такая идея периодически оказывается плодотворной и действительно приводит к новым стимулам. Тем не менее круг идей, сопутствующий такого рода исследованиям, относят к динамике вычислений. Конечно, при совершенно радикальном изучении самого переходного процесса для вычислений с меняющейся средой приходится применять и радикальные теоретические средства. Часто принимают допущение об изменчивости тех объектов, с которыми приходится иметь дело. На практике, располагая или не располагая теоретическим арсеналом, действуют различными способами, не обязательно принимая полярные взаимоисключающие решения, но и взаимодополнительные. В частности, можно просто в качестве отправной точки избрать потребности программирования “изнутри”, а можно, отправляясь “снаружи”, заняться исследованием предметной области, постепенно подходя к ее представлению в виде, приемлемом для программиста. Как правило, требуются описания, и необходимость их постоянного совершенствования ощущается, практически, во всех исследованиях. Когда пытаются устранить второстепенные детали, сопутствующие анализу процесса вычисления, то остаются с представлением об абстракции вычисления. Элементарные “частицы”, участвующие в вычислении, из которых в идеале можно составить сколь угодно обширные блоки или макросы, все еще не теряют привлекательности в глазах теоретиков и практически работающих программистов. Основной конструкцией, которая используется в любой программе и в действительно произвольно взятом исследовании, является функция. Исследования проявления свойств функциональности в различных условиях, похоже, только начинают разворачиваться в полной мере. Начинающий работу в области computer science открывает для себя, фактически, безграничный простор уже имеющихся и интенсивно продолжающихся работ. Идея меняющихся объектов и совокупностей оказывается неизменно плодотворной, наиболее выпукло проявляя себя в денотационной семантике. Воспринимая денотационную семантику как вариант теории вычислений и пользуясь математической идеей продолжения, удается строить языки программирования с модифицируемой средой вычислений.
16