Языки программирования - это системы символов и правил их сочетания, предназначенные для взаимодействия человека со сложными машинами. Существуют сотни таких организованных систем, выполняющих различные функции. Чтобы сориентироваться в этом многообразии, специалисты создают общие классификации языков программирования, основанные на том или ином характерном признаке.
Взаимодействие человека и машины
Важно понять, как происходит общение людей с умными механизмами, прежде чем переходить к классификации языков программирования.
Однажды человек подумал, что машина может выполнять физическую работу за него. Так появился паровой двигатель. Затем предприимчивый человек решил переложить на машину еще и работу умственную. Так появились ЭВМ.
Чтобы достичь какого-то результата, необходимо понимать, как это сделать. В программировании можно выделить несколько этапов решения любой задачи:
- Формализованное логическое описание самой задачи.
- Построение алгоритма, описывающего все шаги достижения цели, начиная от обработки входных данных и заканчивая получением результата.
- Кодирование - составление программы на любом из языков программирования, которую затем можно будет перевести на понятный для компьютера язык.
- Трансляция - непосредственный перевод.
- Сборка работающей программы (исполняемого модуля) из всех составных частей.
Это иерархическая структура, в которой высшие уровни базируются на низших. Без четкой задачи и грамотной проработки алгоритма невозможно создать качественную программу.
Структура языков программирования
Устройство всех систем взаимодействия схоже и во многом определяет классификацию языков программирования.
Основные объекты языка программирования постоянны и напоминают компоненты человеческих языковых систем:
- синтаксис, определяющий формальные правила записи программы, допустимые виды и регистр символов;
- лексика, включающая в себя весь словарный запас языка: имена переменных и функций, константы, строки, операторы;
- грамматика, указывающая, как правильно сочетать единицы языка для образования словосочетаний и предложений.
Лексика и грамматика вместе определяют семантику языка. На этом уровне конкретные последовательности знаков приобретают особый смысл, понятный человеку и компьютеру. Например, слово while во многих системах программирования трактуется как начало циклической операции.
Конечно, компьютеры не понимают обычных слов, само по себе сочетание латинских букв ничего для них не означает. Машины имеют дело с машинным кодом - нулями и единицами, описывающими примитивные состояния наличия или отсутствия сигнала. Поэтому языки программирования устанавливают четкие соответствия определенных слов и последовательностей нативных машинных команд.
Первые примитивные системы управления машинами - перфокарты - использовались для ткацких станков Жаккарда, которые переносили на шелк узор любой сложности. Таким же способом программировались самоиграющие пианино.
Существуют сотни систем программирования, и каждый год появляются новые. Некоторые из них принципиально отличаются друг от друга, другие очень похожи и имеют лишь мелкие особенности. Каждая предназначена для решения своей задачи, широкой или узкоспециализированной.
Обзор классификаций
Языки программирования могут быть сгруппированы по десяткам различных признаков. Они являются принципиально важными или имеют прикладное значение.
Наблюдается сильная зависимость классификации языков программирования от истории развития. С годами технологии усложнялись и кардинально изменялись, на первый план выходило удобство программиста, появлялись эффективные алгоритмы, сложные команды и новые уровни абстракции.
Основные классификации языков программирования по видам и механизму работы базируются на следующих параметрах:
- Особенности лексики и грамматики в совокупности с уровнем абстракции и степенью удобства для человека.
- Базовая концепция и методология составления алгоритмов.
- Способ представления данных.
- Организация процесса взаимодействия с машиной, механизм исполнения программ.
- Область жизни, в которой применяется язык.
- Историческая эпоха, в которую языковая система была сформирована.
Составить четкую классификацию языков и систем программирования нельзя, но возможно разделить и систематизировать их по принципиально важным признакам.
Удобство для человека
Инструкции, написанные на машинном языке, понятны компьютеру, но крайне неудобны для человека. В них трудно разобраться, их практически невозможно быстро изменить или составить с их помощью сложный алгоритм. Для повышения эффективности программисты поднялись на новые уровни абстракции и научили машину принимать более человекопонятные инструкции и самостоятельно переводить их в машинный код. Рассмотрим классификацию и особенности языков программирования разного уровня:
- Машинный код. Это тоже язык программирования, на котором при должной подготовке можно написать инструкцию.
- Низкий уровень. По-настоящему низкоуровневыми являются языки ассемблера, которые используют нативные машинные команды, закодированные с помощью мнемонических кодов.
- Средний уровень. Системы программирования этой группы могут считаться и низко- и высокоуровневыми, в зависимости от конкретных представлений о степень абстракции. Сюда можно отнести C и C++.
- Высокий уровень. Эти языки позволяют создавать сложные алгоритмы, но требуют дополнительной обработки перед выполнением, поэтому сгенерированный ими код менее эффективен и выполняется медленнее.
- Сверхвысокий уровень. Эта немногочисленная группа характеризуется появлением сверхмощных команд и операторов. Сюда можно отнести Алгол-68.
Особенностью языков низкого уровня является их машинозависимость. Они тесно привязаны к особенностям организации конкретного типа ЭВМ, но в целом похожи друг на друга. Они обеспечивают:
- высокую скорость выполнения и максимальную компактность создаваемых программ;
- прямое взаимодействие с аппаратными ресурсами;
- полный контроль над памятью.
Основные недостатки низкоуровневых языков:
- для каждого типа ЭВМ необходимо использовать определенную систему команд, зависящую от особенностей функционирования машины;
- сложность и низкая скорость процесса программирования;
- высокая вероятность появления ошибок, которые сложно отследить;
- отсутствие мобильности программ, невозможность запустить их на ЭВМ другого типа.
Высокоуровневые системы программирования не привязаны к определенной машинной системе команд и могут выполняться на любом компьютере. Благодаря высокому уровню абстракции они могут себе позволить использовать различные концепции и методологии в составлении инструкций. Поэтому классификация языков программирования высокого уровня очень обширна и сложна.
Обработка программы машиной
Чтобы выполнить сложную инструкцию, компьютер прежде всего должен понизить ее абстрактность и перевести на понятный для себя язык. Способ, которым это делается, называется моделью исполнения. Выделяют две основных модели и одну гибридную:
- Компиляция - единовременный перевод всей программы в машинный код.
- Интерпретация - последовательное выполнение каждого выражения.
- Транскомпиляция - перевод на язык более низкого уровня, например C или ассемблер, и его последующая компиляция.
Для перевода необходима специальная программа-транслятор - компилятор или интерпретатор, без которой работа с языком невозможна.
Интерпретатор работает с каждой строчкой программы отдельно, анализируя ее и сразу же выполняя. Его присутствие необходимо от начала и до самого конца работы программы.
Основные недостатки интерпретационной модели:
- постоянное нахождение транслятора в памяти ЭВМ;
- повторная обработка повторяющихся команд.
Несмотря на это, интерпретируемые языки очень удобны для циклической разработки и отладки, так как позволяют быстро вносить изменения в программу.
Компилятор же трудится только один раз, сразу преобразуя всю инструкцию в понятный для компьютера вид - машинный код или некий промежуточный байт-код, а затем покидает память ЭВМ. Здесь выполнение отделено от процесса трансляции, что является более эффективной моделью.
Основные недостатки компиляционной модели:
- большая сложность.
Прежде чем перевести программу на понятный машине язык, транслятор много раз проходит по исходной инструкции, анализируя и проверяя ее.
Четкого разграничения систем не существует, так как традиционно интерпретируемые языки могут быть скомпилированы и наоборот.
Классификация языков программирования высокого уровня по модели исполнения:
- Интерпретируемые - Python, Haskell, PHP, JavaScript.
- Компилируемые сразу в машинный код: C, C++, Fortran, ASM.
- Компилируемые в байт-код: Python, Java.
- Транскомпилируемые: Haskell, Fortran, C, C++.
Обработка данных
Любой язык программирования работает с информацией, которой нужно каким-то образом манипулировать, проверять на корректность, изменять. Данные могут быть самими разнообразными - числами, строками или сложными структурами. Разумеется, с каждым типом необходимо работать по-разному, но чтобы определить, как именно, сначала необходимо понять, с какими именно данными ЭВМ имеет дело.
На основе метода определения разновидности данных построена классификация языков программирования по системе типов.
- Нетипизированные языки.
- Типизированные языки разной степени строгости.
Нетипизированными являются языки ассемблера, которые способны обрабатывать непосредственно двоичные данные. Тип данных при этом не имеет никакого значения.
Для типизированных языков важно, с какой разновидностью данных они работают. Некоторые операции определены только для чисел, например деление, другие - только для строк. При этом некоторые системы позволяют программисту определенные вольности. Например, они могут самостоятельно определять и "неявно" конвертировать один тип данных в другой, исходя из семантики команды. Это очень удобно, но усложняет отладку, так как может привести к незаметной ошибке. Ярким примером нестрогой типизации является JavaScript.
Строго типизированные языки, например, Java, такой свободы не допускают и требуют указания типов и их явной конверсии, если она необходима.
Существует также классификация языков программирования высокого уровня по моменту проверки типов данных:
- статические языки обычно являются компилируемыми. Проверка типов происходит при анализе программы перед ее трансляцией на машинный язык.
- динамические языки проверяют типы данных при выполнении.
Принципиальный способ взаимодействия
Высокоабстрактные языки могут быть разделены по основной парадигме программирования. Существуют десятки методологий составления программ, некоторые из которых очень похожи друг на друга, поэтому создать четкую систему различий невозможно. Кратко классификация языков программирования выглядит так:
- алгоритмические, императивные, процедурные. Требуют явного последовательного описания алгоритма решения задачи. Операторы при этом объединяются в процедурные группы, отделенные от самих данных. Примеры процедурных языков - Pascal, Basic;
- логические, декларативные. Максимально формализовано описывают саму задачу и требуемый результат. Решение при этом должно логически следовать из этого описания;
- объектно-ориентированные, структурированные. Имеют в основе концепцию объекта, объединяющего в себе данные и методы их обработки.
Объекты в программировании
Языки последней группы описывают все сущности в виде независимых объектов, скрывающих в себе сложные механики. Основными концепциями ООП (объектно-ориентированного программирования) являются:
- инкапсуляция - скрытие функционала внутри объекта;
- наследование одними объектами методов других;
- полиморфизм - изменение сути с сохранением внешнего интерфейса.
Можно составить также классификацию языков объектно-ориентированного программирования по способу реализации основных концепций этого подхода - наследования, инкапсуляции и полиморфизма. Помимо классических механизмов существуют другие, например прототипный, используемый в JavaScript.
Методология ООП считается самой прогрессивной, эффективной и в некотором смысле модной. Однако в ряде случаев для решения конкретной задачи более эффективными могут быть другие подходы, например функциональный.
Поколения языков программирования
Классификация языков программирования по истории их появления считается условной, так как не учитывает особенности конкретных систем. Однако она позволяет проследить, как менялись со временем концепции и усложнялись задачи, стоящие перед программистами.
В попытках связать классификацию и эволюцию языков программирования выделились несколько крупных групп, названных поколениями:
- Первое поколение - машинные языки низкого уровня, привязанные к реализации конкретной ЭВМ. "Программы" на этих языках выглядели как ряды переключателей, приведенных в нужное положение, или перфокарты (перфоленты). Таким образом, все команды представляли собой последовательность нулей и единиц - бинарный код. Пример: язык ARM-процессора.
- Во втором поколении языки стали немного понятнее для человека, но отвязать их от конкретного аппарата так и не удалось. Это время языков ассемблера с его мнемоническими кодами и однозначной сборкой в машиночитаемую форму. Пример: Макроассемблер.
- Языки третьего поколения сняли с программиста заботу о непринципиальных деталях составления инструкций, таких как перевод программы в машинный код. Теперь компьютер научился делать это самостоятельно. Синтаксис и лексика приблизились к человеческим, стали понятнее. В этом поколении зародились практически все современные языки высокого уровня с широкой областью применения, независимо от их парадигмы: PHP, Fortran.
- В четвертом поколении уровень абстракции возрос еще больше, резко сужая область использования. К этой группе относятся такие специфичные языки, как FoxPro, Simulink, SQL. Появились языки визуального программирования: CAD-пакеты, системы RAD.
- Наконец, языки пятого поколения должны были сами писать программы, получая лишь описание от программиста. Эта задумка так и не была реализована полностью, так как для составления эффективного алгоритма иногда недостаточно прямой машинной логики, а требуется еще человеческая интуиция и смекалка. Примерами языков пятого поколения являются MathCAD, Prolog и Mercury.
По сути, поколения языков в точности соответствуют этапам программирования, рассмотренным в начале статьи, перечисленным в обратном порядке. Изначально программист все операции брал на себя, а машина лишь выполняла заданную последовательность действий. Теперь же ЭВМ способна выдавать результат по формализованному описанию задачи.
Облегчение труда программиста сопровождается увеличением нагрузки на машину, программа выполняется медленнее и требует больших ресурсов.
Несмотря на то что технический прогресс движется семимильными шагами, языки первых поколений вовсе не исчезли. Они применяются в областях, требующих максимальной простоты и эффективности.
Область применения
Каждый язык программирования хорош в своей области, для которой он создавался. Так, для программирования микроконтроллеров используются ассемблеры, а с Java там делать нечего. Низкоуровневое программирование драйверов эффективно с C, который позволяет строго контролировать ресурсы памяти. Для веб-программирования стоит выбрать скриптовые языки PHP и JavaScript, интерпретатор которого встроен в каждый современный браузер. Важные банковские программы написаны на Java, обеспечивающем контроль ошибок. Аэрокосмические - тоже на Java или на Паскале, который даже уборку мусора отдает на контроль программисту.
Все языки хороши, нужно лишь подобрать соответствующий задаче.
Важность классификации
Сложно разделить сотни существующих систем взаимодействия человека и ЭВМ на несколько четких групп. И все же обзор классификаций языков программирования кратко прослеживает историю их эволюции и глубже раскрывает заложенные в них идеи.
Каждый язык одновременно относится к нескольким из перечисленных групп - он может быть строго типизированным, компилируемым и объектно-ориентированным одновременно. Поэтому нельзя рассматривать многообразие систем программирования через призму какой-то одной классификации.