М. Бен-Ари - Языки программирования. Практический сравнительный анализ (2000) (1160781), страница 3
Текст из файла (страница 3)
• Ada расширяет область применения языков программирования, обеспечивая обработку ошибок и параллельное программирование, что традиционно считалось (нестандартными) функциями операционных систем.
Несмотря на техническое совершенство и преимущества ранней стандартизации, язык Ada не достиг большой популярности вне военных и других крупномасштабных проектов (типа коммерческой авиации и железнодорожных перевозок). Язык Ada получил репутацию трудного. Это связано с тем, что он поддерживает многие аспекты программирования (параллелизм, обработку исключительных ситуаций, переносимые числовые данные), которые другие языки (подобные С и Pascal) оставляют операционной системе. На самом деле его нужно просто больше изучать. К тому же первоначально были недоступны хорошие и недорогие среды разработки для сферы образования. Теперь, когда есть бесплатные компиляторы (см. приложение А) и хорошие учебники, Ada все чаще и чаще встречается в учебных курсах даже как «первый» язык.
Ada 95
Ровно через двенадцать лет после принятия в 1983 г. первого стандарта языка Ada был издан новый стандарт. В новой версии, названной Ada 95, исправлены некоторые ошибки первоначальной версии, но главное — это добавление поддержки настоящего объектно-ориентированного программирования, включая наследование, которого не было в Ada 83, так как его считали неэффективным. Кроме того, Ada 95 содержит приложения, в которых описываются стандартные (но необязательные) расширения для систем реального времени, распределенных систем, информационных систем, защищенных систем, а также «числовое» (numerics) приложение.
В этой книге название «Ada» будет использоваться в тех случаях, когда обсуждение не касается особенностей одной из версий: Ada 83 или Ada 95. Заметим, что в литературе Ada 95 упоминалась как Ada 9X, так как во время разработки точная дата стандартизации не была известна
.
1.3. Языки, ориентированные на данные
На заре программирования было создано и реализовано несколько языков, значительно повлиявших на дальнейшие разработки. У них была одна общая черта: каждый язык имел предпочтительную структуру данных и обширный набор команд для нее. Эти языки позволяли создавать сложные программы, которые трудно было бы написать на языках типа Fortran, просто манипулирующих компьютерными словами. В следующих подразделах мы рассмотрим некоторые из этих языков.
Lisp
Основная структура данных в языке Lisp — связанный список. Первоначаль-но Lisp был разработан для исследований в теории вычислений, и многие работы по искусственному интеллекту были выполнены на языке Lisp. Язык был настолько важен, что компьютеры разрабатывались и создавались так, чтобы оптимизировать выполнение Lisp-программ. Одна из проблем языка состояла в обилии различных «диалектов», возникавших по мере того, как язык реализовывался на различных машинах. Позже был разработан стандартный язык Lisp, чтобы программы можно было переносить с одного компьютера на другой. В настоящее время популярен «диалект» языка Lisp — CLOS, поддерживающий объектно-ориентированное программирование.
Три основные команды языка Lisp — это car(L) и cdr(L), которые извлекают, соответственно, начало и конец списка L, и cons(E, L), которая создает новый список из элемента Е и существующего списка L. Используя эти команды, можно определить функции обработки списков, содержащих нечисловые данные; такие функции было бы довольно трудно запрограммировать на языке Fortran.
Мы не будем больше обсуждать язык Lisp, потому что многие его основополагающие идеи были перенесены в современные функциональные языки программирования типа ML, который мы обсудим в гл. 16.
APL
Язык APL является развитием математического формализма, который используется для описания вычислений. Основные структуры данных в нем — векторы и матрицы, и операции выполняются над такими структурами непосредственно, без циклов. Программы на языке APL очень короткие по сравнению с аналогичными программами на традиционных языках. Применение APL осложняло то, что в язык перешел большой набор математических символов из первоначального формализма. Это требовало специальных терминалов и затрудняло экспериментирование с APL без дорогостоящих аппаратных средств. Современные графические интерфейсы пользователя, применяющие программные шрифты, решили эту проблему, которая замедляла принятие APL.
Предположим, что задана векторная переменная:
V= 1 5 10 15 20 25
Операторы языка APL могут работать непосредственно с V без записи циклов с индексами:
+ /V =76 Свертка сложением(суммирует элементы)
фV = 25 20 15 10 5 1 Обращает вектор
2 3 pV = 1 5 10 Переопределяет размерность
V 15 20 25 как матрицу 2x3
Векторные и матричные сложения и умножения также можно выполнить непосредственно над такими переменными.
Snobol, Icon
Первые языки имели дело практически только с числами. Для работы в таких областях, как обработка естественных языков, идеально подходит Snobol (и его преемник Icon), поскольку их базовой структурой данных является строка. Основная операция в языке Snobol сравнивает образец со строкой, и побочным результатом совпадения может быть разложение строки на подстроки. В языке Icon основная операция — вычисление выражения, причем выражения включают сложные операции со строками.
В языке Icon есть важная встроенная функция find(s1, s2), которая ищет вхождения строки s1 в строку s2. В отличие от подобных функций языка С find генерирует список всех позиций в s2, в которых встречается s1:
line := 0 # Инициализировать счетчик строк while s := read() { # Читать до конца файла
every col := find("the", s) do # Генерировать позиции столбца write (line, " ",col) # Write(line,col) для "the"
line := line+ 1
}
Эта программа записывает номера строк и столбцов всех вхождений строки "the" в файл. Если команда find не находит ни одного вхождения, то она «терпит неудачу» (fail), и вычисление выражения завершается. Ключевое слово every вызывает повторение вычисления функции до тех пор, пока оно завершается успешно.
Выражения Icon содержат не только строки, которые представляют собой последовательности символов; они также определены над наборами символов csets. Таким образом
vowels := 'aeiou'
присваивает переменной vowel (гласные) значение, представляющее собой набор указанных символов. Это можно использовать в функциях типа upto(vowels,s), генерирующих последовательность позиций гласных в s, и many(vowels,s), возвращающих самую длинную начальную последовательность гласных в s.
Более сложная функция bal подобна upto за исключением того, что она генерирует последовательности позиций, которые сбалансированы по «скобочным» символам:
bal(' +-*/','([', ')]',*)
Это выражение могло использоваться в компиляторе, чтобы генерировать сбалансированные арифметические подстроки. Если в качестве строки s задать
"х + (у [u/v] - 1 )*z", вышеупомянутое выражение сгенерирует индексы, соответствующие подстрокам:
x
x+(y[u/v]-1
Первая подстрока сбалансирована, так как она заканчивается «+» и не содержит никаких скобок; вторая подстрока сбалансирована, поскольку она завершается символом «*» и имеет квадратные скобки, правильно вложенные внутри круглых скобок.
Так как вычисление выражения может быть неуспешным (fail), используется откат (backtracking), чтобы продолжить поиск от предыдущих генерирующих функций. Следующая программа печатает вхождения гласных, за исключением тех, которые начинаются в столбце 1 .
line := 0 # Инициализировать счетчик строк while s := read() { # Читать до конца файла every col := (upto (vowels, line) > 1 ) do
# Генерировать позиции столбца write (line, " ",col) # write(line,col) для гласных
line := line + 1
}
Функция поиска генерирует индекс, который затем проверяется на «>». Если проверка неуспешна (не говорите: «если результат ложный»), программа возвращает управление генерирующей функции upto, чтобы получить новый индекс.
Icon — удобный язык для программ, выполняющих сложную обработку строк. В нем происходит абстрагирование от большей части явных индексных вычислений, и программы оказываются очень короткими по сравнению с обычными языками для числового или системного программирования. Кроме того, в Icon очень интересен встроенный механизм генерации и отката, который предлагает более развитый уровень абстракции управления.
SETL
Основная структура данных в SETL — множество. Так как множество — наиболее общая математическая структура, с помощью которой определяются все другие математические структуры, то SETL может использоваться для создания программ высокой степени общности и абстрактности и поэтому очень коротких. Такие программы имеют сходство с логическими программами (гл. 17), в которых математические описания могут быть непосредственно исполняемыми. В теории множеств используется нотация: {х \р(х)}, обозначающая множество всех х таких, что логическое выражение р(х) является истиной. Например, множество простых чисел в этой нотации может быть записано как
{ п \ -,3 т [(2<т<п— 1) л (nmodm = 0)]}
Эта формула читается так: множество натуральных чисел п таких, что не cуществует натурального т от 2 до п — 1 , на которое п делится без остатка.
Чтобы напечатать все простые числа в диапазоне от 2 до 100, достаточно «протранслировать» это определение в однострочную программу на языке SETL:
print ({n in {2.. 100} | not exists m in {2.. n — 1} | (n mod m) = 0});
Все эти языки объединяет то, что к их разработке подходили больше с математических, чем с инженерных позиций. То есть решалась задача, как смоделировать известную математическую теорию, а не как выдавать команды для процессора и памяти. Такие продвинутые языки очень полезны для трудно программируемых задач, где важно сосредоточить внимание на проблеме, а не на деталях реализации.
Языки, ориентированные на данные, сейчас несколько менее популярны, чем раньше, отчасти потому, что объектно-ориентированные методы позволяют внедрить операции, ориентированные на данные, в обычные языки типа C++ и Ada, а также из-за конкуренции более новых языковых концепций, таких как функциональное и логическое программирование. Тем не менее эти языки интересны с технической точки зрения и вполне подходят для решения задач, для которых они были разработаны. Студентам рекомендуется приложить усилие и изучить один или несколько таких языков, потому что это расширит их представление о том, как может быть структурирован язык программирования.
1.4. Объектно-ориентированные языки
Объектно-ориентированное программирование (ООП) — это метод структурирования программ путем идентификации объектов реального мира или других объектов и написания модулей, каждый из которых содержит все данные и исполняемые команды, необходимые для представления одного класса объектов. Внутри такого модуля существует отчетливое различие между абстрактными свойствами класса, которые экспортируются для использования другими объектами, и реализацией, которая скрыта таким образом, что может быть изменена без влияния на остальную часть системы.
Первый объектно-ориентированный язык программирования Simula был создан в 1960-х годах К. Нигаардом и О.-Дж. Далом для моделирования систем: каждая подсистема, принимающая участие в моделировании, программировалась как объект. Так как возможно существование нескольких экземпляров одной подсистемы, то можно запрограммировать класс для описания каждой подсистемы и выделить память для объектов этого класса.
Исследовательский центр Xerox Palo Alto Research Center популяризировал ООП с помощью языка Smalltalk. Такие же исследования вели к системам окон, так популярным сегодня, но важное преимущество Smalltalk заключается в том, что это не только язык, но и полная среда программирования. В техническом плане Smalltalk был достижением как язык, в котором классы и объекты являются единственными конструкциями структурирования, так что нет необходимости встраивать эти понятия в «обычный» язык.
Технический аспект этих первых объектно-ориентированных языков помешал более широкому распространению ООП: отведение памяти, диспетчеризация операций и контроль соответствия типов осуществлялись динамически (во время выполнения), а не статически (во время компиляции). Не вдаваясь в детали (см. соответствующий материал в гл. 8 и 14), отметим, что в итоге программы на этих языках связаны с неприемлемыми для многих систем накладными расходами по времени и памяти. Кроме того, статический контроль соответствия типов (см. гл. 4) теперь считается необходимым для разработки надежного программного обеспечения. По этим причинам в языке Ada 83 реализована только частичная поддержка ООП.
Язык C++ показал, что можно реализовать полный механизм ООП способом, который совместим со статическим распределением памяти и контролем соответствия типов и с фиксированными затратами на диспетчеризацию: динамические механизмы ООП используются только, если они необходимы до существу. Поддержка ООП в Ada 95 основана на тех же идеях, что и в C++.
Однако нет необходимости «прививать» поддержку ООП в существующие языки, чтобы получить эти преимущества. Язык Eiffel подобен Smalltalk в том, что единственным методом структурирования является метод классов и объектов, а также подобен C++ и Ada 95 в том, что проверка типов статическая, а реализация объектов может быть как статической, так и динамической, если нужно. Простота языка Eiffel по сравнению с гибридами, которым «привита» полная поддержка ООП, делает его превосходным выбором в качестве первого языка программирования.
Мы обсудим языковую поддержку ООП более подробно, сначала в C++, а затем в Ada 95. Кроме того, краткое описание Eiffel покажет, как выглядит «чистый» язык ООП.
1.5. Непроцедурные языки