Языки и системы программирования. Развитие языков программирования. Поколения языков программирования
История программирования восходит к Аде Лавлейс, которая ещё в середине XIX века наметила такие понятия как подпрограмма, библиотека подпрограмм, регистр, цикл и другие. В то время все эти понятия формулировались применительно к разностной машине Бэббиджа, что отрицательным образом сказывалось на их общности и формальности, ведь приходилось привязываться к конкретным техническим решениям механической машины. Поэтому в 1936 году английский математик Алан Тьюринг предложил понятие абстрактного автомата, позднее названного в его честь машиной Тьюринга. Машина Тьюринга состоит из бесконечной ленты, разделённой на ячейки, по которой движется управляющее устройство. Управляющее устройство может считать символ, хранящийся в ячейке, и в зависимости от того, каким будет этот символ, переместиться вправо или влево, или остаться на месте, но при этом записать в ячейку новое значение. Конкретные правила движения управляющего устройства определяются алгоритмом, реализуемым данной машиной Тьюринга.
Тезис Чёрча-Тьюринга гласит, что любая функция, которая может быть вычислена физическим устройством, может быть вычислена машиной Тьюринга. А потому если какая-то задача не может быть решена с помощью машины Тьюринга, она не может быть решена вообще. Разумеется, никто не занимается решением практических задач с помощью моделей машины Тьюринга, для практических задач существуют языки программирования. Отсюда появляется понятие полноты по Тьюрингу: тьюринг-полным языком называется язык, всем элементом которого можно поставить в соответствие алгоритмы для машины Тьюринга. Все применяемые на практике языки программирования являются тьюринг-полнымb языками программирования.
Несмотря на то, что указанные понятия верны для самых разных вычислительных машин, даже таких как механические, аналоговые, машины с троичной логикой и многих других, на практике подавляющее большинство вычислительных машин относятся к фон-неймановской архитектуре. Фон-неймановской машиной называют ЭВМ, построенную по следующим критериям:
• состоит из арифметико-логического устройства (АЛУ), устройства управления (УУ), запоминающего устройства, устройств ввода-вывода.
• АЛУ и УУ, объединённые в центральный процессор (ЦП), определяют действия, подлежащие выполнению, считывая команды из оперативной памяти (ОЗУ).
• машина работает в двоичном коде.
• программы и данные хранятся в одном и том же пространстве памяти.
Из пунктов 3 и 4 становится ясно, что программа для фон-неймановской машины представляет собой последовательность нулей и единиц, одни участки которой кодируют команды, а другие – данные. Подобная последовательность называется машинным кодом. Если учесть, что любое число в машинном представлении кодируется фиксированным количеством разрядов (неиспользуемые заполняются нулями), число которых может доходить до 64, можно понять, что программировать в машинном коде невероятно сложно. Поэтому в 1950 году Грейс Хоппер создаёт для ЭВМ «Эдсак» первый язык ассемблера.
Язык ассемблера пре6дставляет собой удобную для восприятия человеком форму записи машинных команд. Между машинным кодом и кодом на языке ассемблера существует взаимно однозначное соответствие. Тем не менее, стоит заметить, что современные ассемблеры предоставляют программисту базовый уровень абстракции. Во-первых, это переменные и метки с символьными именами, в противоположность тому, что в машинном коде на их местах стоят адреса в памяти. Во-вторых, это независимость мнемокода команды от её операндов. Например, с точки зрения процессора, присваивание одной переменной значения другой переменной и присваивание переменной значения, записанного в коде программы – это разные инструкции, но в языке ассемблера они обозначаются одним мнемокодом mov, а выбор ассемблером конкретной инструкции зависит от формата записи операндов.
Несмотря на то, что язык ассемблера существенно упростил написание программ, этот процесс всё ещё требовал больших знаний в области организации конкретной ЭВМ, для которой писалась программа. А программирование в то время требовалось в основном учёным и военным – то есть людям, которые не могут тратить много времени на овладение навыками программирования. Поэтому в 1958 году Джон Бэкус разрабатывает в корпорации IBM язык Фортран (от англ. FORmulaTRANslator), тем самым начиная историю развития языков высокого уровня.
Язык программирования высокого уровня – это язык программирования, в который введены неочевидные и часто неоднозначные в машинном коде смысловые конструкции. Перевод текста на языке высокого уровня в машинный код осуществляет специальная программа, называемая компилятором. Грамматика языка высокого уровня определяется не особенностями ЭВМ, а тем, какие классы задач предполагается решать на данном языке.
Можно составить некоторую классификацию языков программирования. Разумеется, следует понимать, что здесь приведены лишь наиболее известные языки программирования, всего же их более восьми с половиной тысяч.
Следует пояснить терминологию, использованную в данной схеме. Все языки программирования высокого уровня делятся на две группы: декларативные и императивные:
Программа на императивном языке программирования с математической точки зрения представляет собой общее решение поставленной задачи, иными словами, ответ на вопрос «как делать?». Это последовательность команд, которые должен выполнить исполнитель.
Программа на декларативномязыке программирования является сочетанием формализованной в рамках языка программирования задачей и всех необходимых для её решения теорем, проще говоря, ответ на вопрос «что делать?». Конкретную последовательность выполняемых действий выполняет компилятор, или чаще интерпретатор– программа, в реальном времени выполняющая код программы без его преобразования в машинный код.
Поскольку интерпретаторы декларативных языков сами решают поставленные задачи, возникает вопрос, какими методами им пользоваться. Языки, работающие в рамках аппарата дискретной математики, называют функциональными, а работающие с математической логикой – логическими. Декларативные языки программирования чрезвычайно сложны для понимания, программы на них трудно отлаживать из-за неочевидной последовательности выполнения операций, однако они являются наиболее перспективными в области написания систем с искусственным интеллектом.
Важнейшей чертой программ на императивных языках программирования является их структурность, то есть разбиение основной задачи на небольшие подзадачи и их отдельное решение. Есть два пути реализации такой парадигмы: подпрограммы и объекты.
В процедурных языках основной программ служат подпрограммы, принимающие на вход какие-то данные, производящие с ними определённые действия и возвращающие результаты этих действий. Процедурные языки исторически были первыми, и до сих пор сохраняют свои позиции: Бейсик и Паскаль являются самыми распространёнными языками для обучения программированию, на Фортране производиться основная часть научных расчётов, Си является основным языком системного программирования.
Совершенно иной подход реализуется в объектно-ориентированных языках. Объект – это набор данных, для которого определены правила поведения, называемые методами, зависящими о того, к какому классу принадлежит данный объект. Это позволяет основную часть времени написания программы находиться в рамках абстрактных понятий, заботясь о конкретной реализации лишь на последнем этапе. В зависимости от реализации возможностей объектно-ориентированного программирования языки делятся на две группы: традиционные (так же называемые основанными на классах), в которых сначала задаётся структура классов, а затем создаются объекты, принадлежащие этим классам, и прототипные, в которых сначала создаётся объект-прототип, а затем на его основе создаются новые, таким образом формируя класс.
Языки программирования принято делить на пять поколений. В первое поколение входят языки, созданные в начале 50-х годов, когда только появились первые компьютеры. Это был первый язык ассемблера, созданный по принципу "одна инструкция – одна строка".
Расцвет второго поколения языков программирования пришелся на конец 50-х – начало 60-х годов. Тогда был разработан символический ассемблер, в котором появилось понятие переменной. Он стал первым полноценным языком программирования. Благодаря его возникновению заметно возросли скорость разработки и надежность программ.
Появление третьего поколения языков программирования принято относить к 60-м годам. В это время возникли универсальные языки высокого уровня, с их помощью удается решать задачи из любых областей. Такие качества новых языков, как относительная простота, независимость от конкретного компьютера и возможность использования мощных синтаксических конструкций, позволили резко повысить производительность труда программистов. Понятная большинству пользователей структура этих языков привлекла к написанию небольших программ (как правило, инженерного или экономического характера). Подавляющее большинство языков этого поколения успешно применяется и сегодня.
С начала 70-х годов по настоящее время продолжается период языков четвертого поколения. Эти языки предназначены для реализации крупных проектов, повышения их надежности и скорости создания. Они ориентированы на специализированные области применения, где хороших результатов можно добиться, используя не универсальные, а проблемно-ориентированные языки, оперирующие конкретными понятиями узкой предметной области. Как правило, в эти языки встраиваются мощные операторы, позволяющие одной строкой описать такую функциональность, для реализации которой на языках младших поколений потребовались бы тысячи строк исходного кода.
Рождение языков пятого поколения произошло в середине 90-х годов. К ним относятся также системы автоматического создания прикладных программ с помощью визуальных средств разработки, без знания программирования. Главная идея, которая закладывается в эти языки – возможность автоматического формирования результирующего текста на универсальных языках программирования (который потом требуется откомпилировать). Инструкции же вводятся в компьютер в максимально наглядном виде с помощью методов, наиболее удобных для человека, не знакомого с программированием.