Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Языки программирования. Практический сравнитель...doc
Скачиваний:
54
Добавлен:
09.09.2019
Размер:
2.68 Mб
Скачать

1.8. Вычислимость

В 1930-х годах, еще до того, как были изобретены компьютеры, логики иссле­довали абстрактные концепции вычисления. Алан Тьюринг и .Алонзо Черч независимо предложили чрезвычайно простые модели вычисления (назван­ные соответственно машинами Тьюринга и Лямбда-исчислением) и затем вы­двинули следующее утверждение (известное как Тезис Черча —Тьюринга):

Любое исполнимое вычисление может быть выполнено на любой из этих моделей.

Машины Тьюринга чрезвычайно просты; если воспользоваться синтак­сисом языка С, то объявления данных будут выглядеть так:

char tape[...];

int current = 0;

где лента (tape) предполагается бесконечной. Программа состоит из любого числа операторов вида:

L17: if (tape[currentj == 'g') {

tape[current++] = 'j'i

goto L43;

}

Оператор машины Тьюринга выполняется за четыре следующих шага.

• Считать и проверить символ в текущей ячейке ленты.

• Заменить символ на другой символ (необязательно).

• Увеличить или уменьшить указатель текущей ячейки.

• Перейти к другому оператору.

Согласно Тезису Черча — Тьюринга, любое вычисление, которое действи­тельно можно описать, может быть запрограммировано на этой примитивной машине. Интуитивная очевидность Тезиса опирается на два утверждения:

• Исследователи предложили множество моделей вычислений, и было до­казано, что все они эквивалентны машинам Тьюринга.

• Никому пока не удалось описать вычисление, которое не может быть ре­ализовано машиной Тьюринга.

Так как машину Тьюринга можно легко смоделировать на любом языке программирования, можно сказать, что все языки программирования «дела­ют» одно и то же, т. е. в некотором смысле эквивалентны.

1.9. Упражнения

1. Опишите, как реализовать компилятор для языка на том же самом язы­ке («самораскрутка»).

2. Придумайте синтаксис для APL-подобного языка для матричных вы­числений, используя обычные символы.

3. Составьте список полезных команд над строками и сравните ваш список со встроенными командами языков Snobol и Icon.

4. Составьте список полезных команд над множествами и сравните ваш список со встроенными командами языка SETL.

5. Смоделируйте (универсальную) машину Тьюринга на нескольких язы­ках программирования.

Глава 2

Элементы

языков программирования

2.1. Синтаксис

Как и у обычных языков, у языков программирования есть синтаксис:

Синтаксис языка (программирования) — это набор правил, которые опре­деляют, какие последовательности символов считаются допустимыми вы­ражениями (программами) в языке.

Синтаксис задается с помощью формальной нотации.

Самая распространенная формальная нотация синтаксиса — это расширен­ная форма Бекуса — Наура (РБНФ). В РБНФ мы начинаем с объекта самого верхнего уровня, с программы, и применяем правила декомпозиции объектов, пока не достигнем уровня отдельного символа. Например, в языке С синтак­сис условного оператора (if-оператора) задается правилом:

if-onepamop :: = if (выражение) оператор [else оператор]

Имена, выделенные курсивом, представляют синтаксические категории, а имена и символы, выделенные полужирным шрифтом, представляют факти­ческие символы, которые должны появиться в программе. Каждое правило содержит символ «:: =», означающий «представляет собой». Прочие символы используются для краткости записи:

[ ] Не обязательный {} Ноль или более повторений | Или

Таким образом, else-оператор в if-операторе не является обязательным. Использование фигурных скобок можно продемонстрировать на (упрощен­ном) правиле для объявления списка переменных:

Объявление-переменной ::= спецификатор-типа идентификатор {, идентификатор};

Это читается так: объявление переменной представляет собой специфика­тор типа, за которым следует идентификатор (имя переменной) и необяза­тельная последовательность идентификаторов, предваряемых запятыми, в конце ставится точка с запятой.

Правила синтаксиса легче изучить, если они заданы в виде диаграмм (рис. 2.1). Круги или овалы обозначают фактические символы, а прямоугольники — синтаксические категории, которые имеют собственные диаграммы.

.

Последовательность символов, получаемых при последовательном прохожде­нии пути на диаграммах, является (синтаксически) правильной программой.

Хотя многие программисты страстно привязаны к синтаксису опреде­ленного языка, этот аспект языка, пожалуй, наименее важен. Любой разумный синтаксис легко изучить; кроме того, синтаксические ошибки об­наруживаются компилятором и редко вызывают проблемы с работающей программой. Мы ограничимся тем, что отметим несколько возможных син­таксических ловушек, которые могут вызвать ошибки во время выполнения программы:

Будьте внимательны с ограничениями на длину идентификаторов. Ес­ли значимы только первые 10 символов, то current_winner и current _width будут представлять один и тот же идентификатор.

Многие языки не чувствительны к регистру, то есть СЧЕТ и счет пред-ставляют одно и то же имя. Язык С чувствителен к регистру, поэтому эти имена представляют два разных идентификатора. При разработке чувст­вительных к регистру языков полезно задать четкие соглашения по ис­пользованию каждого регистра, чтобы случайные опечатки не приводи­ли к ошибкам. Например, по одному из соглашений языка С в програм­ме все записывается на нижнем регистре за исключением определенных имен констант, которые задаются на верхнем регистре.

Существуют две формы комментариев: комментарии в языках Fortran, Ada и C++ начинаются с символа (С, - -, и //, соответственно) и распро­страняются до конца строки, в то время как а языках С и Pascal коммента­рии имеют как начальный, так и конечный символы: /* ... */ в.С и (* ... *) иди {...} в Pascal. Вторая форма удобна для «закомментаривания» неис-пользуемого кода (который, взможнo, был вставлен для тестированя), но при этом существует опасность пропустить конечный символ, в результате чего будет пропущена последовательность операторов:

с

/* Комментарий следовало бы закончить здесь

а = b + с; Оператор будет пропущен

/*...*/ Здесь конец комментария

Остерегайтесь похожих, но разных символов. Если вы когда-либо изуча­ли математику, то вас должно удивить, что знакомый символ «=» ис­пользуется в языках С и Fortran как оператор присваивания, в то время как новые символы «==» в С и «.eq.» в Fortran используются в качестве операции сравнения на равенство. Стремление написать:

с

if(a=b)...

является настолько общим, что многие компиляторы выдадут предуп­реждение, хотя оператор имеет допустимое значение.

В качестве исторического прецедента напомним известную проблему с синтаксисом языка Fortran. Большинство языков требует, чтобы слова в программе отделялись одним или несколькими пробелами (или другими пробельными символами типа табуляции), однако в языке Fortran пробель­ные символы игнорируются. Рассмотрим следующий оператор, который определяет «цикл до метки 10 при изменении индекса i от 1 до 100»:

Fortan


do 10 i = 1,100

Если запятая случайно заменена точкой, то этот оператор становится на самом деле.оператором присваивания, присваивая 1.100 переменной, имя которой образуется соединением всех символов перед знаком «=»:

Fortan


do10i = l.TOO

Говорят, эта ошибка заставила ракету взорваться до запуска в космос!