Файл: Классификация языков программирования. Критерии выборы среды и языка разработки программ.pdf
Добавлен: 30.03.2023
Просмотров: 1136
Скачиваний: 9
СОДЕРЖАНИЕ
1. Понятие языка программирования
2. Классификация языков программирования
2.1. Классификация языков программирования по уровню
2.2. Классификация языков программирования по поколению
2.3. Классификация языков программирования по функциональному признаку
2.4. Классификация языков программирования в зависимости от способа реализации
В компьютерных программах, написанных на языках логического программирования, вообще не описывается действий. Программист задаёт данные и отношения между ними, после этого программе можно задать вопрос, компьютер перебирает заданные данные и находит ответ. Классический пример языка логического программирования – Prolog. Программа на Прологе содержит, набор предикатов-утверждений, которые образуют проблемно-ориентированную базу данных и правила, имеющие вид условий.
Часто отдельным классом выделяют объектно-ориентированные языки программирования, хотя они содержат элементы процедурного программирования. В языках объектно-ориентированного программирования переменные и функции группируются в классы, благодаря чему достигается высокий уровень структуризации программы. Объекты, порождённые от классов, вызывают методы (функции) друг друга и, таким образом, меняют состояние свойств (переменных). С формальной стороны объектно-ориентированное программирование базируется на процедурной модели, но с содержательной – оно базируется не на функции, а на объекте, как на целостной системе. Объектно-ориентированные языки, благодаря богатому пользовательскому интерфейсу, предлагают человеку решить задачу в удобной для него форме.
2.4. Классификация языков программирования в зависимости от способа реализации
Можно выделить три принципиально разных способа реализации языков программирования:
– компиляция,
– интерпретация,
– встраивание.
Распространено заблуждение, согласно которому способ реализации является присущим конкретному языку свойством. В действительности, это деление до определённой степени условно. В ряде случаев язык программирования имеет формальную семантику, ориентированную на интерпретацию, но все или почти все его действительные реализации являются компиляторами, порой весьма эффективно оптимизирующими (примерами могут служить языки семейства ML, такие как Standard ML, Haskell). Есть языки, размывающие границы между интерпретацией и компиляцией – например, Forth.
Компиляция означает, что исходный код компьютерной программы сперва преобразуется в целевой (машинный) код специальной программой, называемой компилятором. В результате получается исполнимый модуль, который уже может быть запущен на исполнение как отдельная программа.
Интерпретация же означает, что исходный код программы выполняется непосредственно, команда за командой (иногда – с минимальной подготовкой, буквально после разбора исходного кода в AST), – так что программа просто не может быть запущена без наличия интерпретатора.
Встраивание языка можно философски рассматривать как «реализацию без трансляции» – в том смысле, что такой язык является синтаксическим и семантическим подмножеством некоего другого языка, без которого он не существует. Говоря же более точно, встраиваемые языки добавляют к сказанному ещё четыре способа реализации.
Естественный для языка способ реализации определяется временем связывания программных элементов с их характеристиками. В частности, в языках со статической типизацией переменные и другие объекты программы связываются с типом данных на этапе компиляции, а в случае типизации динамической – на этапе выполнения, как правило – в произвольной точке программы. Некоторые свойства элементов языка, такие как значение арифметических операторов или управляющих ключевых слов, могут быть связаны уже на этапе определения языка. В других языках возможно их переназначение. Раннее связывание обычно означает большую эффективность программы, в то время как позднее – большую гибкость, ценой которого является меньшая скорость и/или усложнение соответствующего этапа. Однако, даже очевидных случаев есть исключения – например, интенсиональный полиморфизм откладывает обработку статической типизации до этапа выполнения, но не замедляя, а повышая общее быстродействие (по крайней мере, в теории).
Для любого традиционно компилируемого языка (такого как Паскаль) можно написать интерпретатор. Но многие интерпретируемые языки предоставляют некоторые дополнительные возможности, такие как динамическая генерация кода, так что их компиляция должна быть динамической (см. динамическая компиляция). Таким образом, составной термин «язык плюс способ его реализации» в ряде случаев оказывается уместен. Кроме того, большинство современных «чистых» интерпретаторов не исполняют конструкции языка непосредственно, а компилируют их в некоторое высокоуровневое промежуточное представление (например, с разыменованием переменных и раскрытием макрокоманд). Большинство традиционно интерпретируемых или компилируемых языков могут реализовываться как встраиваемые, хотя метаязыков, которые были бы способны охватить другие языки как своё подмножество, не так много (наиболее ярким представителем является Lisp).
Как правило, скомпилированные программы выполняются быстрее и не требуют для выполнения дополнительных программ, так как уже переведены на машинный язык. Вместе с тем, при каждом изменении текста программы требуется её перекомпиляция, что замедляет процесс разработки. Кроме того, скомпилированная программа может выполняться только на том же типе компьютеров и, как правило, под той же операционной системой, на которую был рассчитан компилятор. Чтобы создать исполняемый файл для машины другого типа, требуется новая компиляция.
Интерпретируемые языки позволяют запускать программы сразу же после изменения, причём на разных типах машин и операционных систем без дополнительных усилий, а гомоиконные – и вовсе динамически перемещать программу между разными машинами без прерывания её работы (наиболее общий случай сериализации), позволяя разрабатывать системы непрерывной доступности. Портируемость интерпретируемой программы определяется только наличием реализаций интерпретаторов под те или иные аппаратные платформы. Ценой всего этого становятся заметные потери быстродействия; кроме того, если компьютерная программа содержит фатальную ошибку, то об этом не будет известно, пока интерпретатор не дойдёт до её места в коде (в отличие от статически типобезопасных языков).
Некоторые языки, например, Java и C#, находятся между компилируемыми и интерпретируемыми. А именно, программа компилируется не в машинный язык, а в машинно-независимый код низкого уровня, байт-код. Далее байт-код выполняется виртуальной машиной. Для выполнения байт-кода обычно используется интерпретация, хотя отдельные его части для ускорения работы программы могут быть транслированы в машинный код непосредственно во время выполнения программы по технологии компиляции «на лету» (Just-in-time compilation, JIT). Для Java байт-код исполняется виртуальной машиной Java (Java Virtual Machine, JVM), для C# – Common Language Runtime. Подобный подход в некотором смысле позволяет использовать плюсы как интерпретаторов, так и компиляторов.
3. Критерии выбора языка программирования
На начальном этапе создания программы так или иначе становится вопрос выбора языка программирования. На данный момент существует огромное количество языков программирования, и каждый из них создавался с определенной целью. Выбор языка программирования во время создания программы является очень важным моментом, от которого зависит многое – скорость создания программы, скорость тестирования, возможность переноса на другие платформы, возможность быстрого внесения изменений, быстрота выполнения конечного продукта и так далее. При этом следует отметить, что идеального языка не существует, все они обладают своими положительными и отрицательными качествами, которые будут так или иначе влиять на процесс разработки.
Рассмотрим критерии выбора языка программирования.
1. Скорость работы конечного продукта. Требовательным к скорости выполнения могут быть программы с большим объемом математических вычислений, например, моделирование физических систем, расчеты большого объема экономических данных, выведение трехмерной графики и прочее. Для данных целей хорошо подойдут компилируемые языки: ассемблер, С, С++, фортран и т.д. После сборки программа не требует ничего лишнего и содержит в себе машинные команды, которые выполняются без лишних задержек. Схема работы таких программ такая:
а) программа исполняется сразу, так сказать она самодостаточна и не требует дополнительных библиотек;
б) программа кроме своего кода содержит вызовы библиотек с машинным кодом (как системных, так и входящих в проект), поэтому, кроме исполнения собственно своих команд, программа вызывает функции из библиотек;
в) в дополнение предыдущим случаям, программа может работать через прослойку драйверов, которые написаны на языках низкого уровня и работают по умолчанию быстро. Как видно, максимум в схеме возможны четыре блока: программа, библиотеки, драйвера, железо.
2. Объем занимаемой оперативной памяти. Данное требование появляется, когда программа разрабатывается для встраиваемых систем, мобильных платформ, микроконтроллеров и так далее. В данных случаях, чем меньше памяти расходует программа на данном языке – тем лучше. К таким языкам, опять же, относятся ассемблер, С, С++, Objective-C и другие. Список языков подобен списку пункта 1, так как чем меньше функциональных блоков в схеме исполнения, тем меньше занимается и память компьютера. Если данное требование некритично, то можно использовать «высокоуровневые языки».
3. Скорость разработки программы. Данное требование возникает тогда, когда необходимо быстро создавать программы. В этом случае выбор падает на высокоуровневые языки с максимально человеколюбивым синтаксисом. Это, например, Java, Flash и подобные. На данных языках время разработки может существенно сокращаться из-за обилия сторонних библиотек, максимально «очеловеченного» синтаксиса, и подобных вещей. Скорость выполнения программ, написанных на данных языках страдает, причем порой весьма ощутимо. Схема выполнения на примере Java: программа в виде байт-кода – виртуальная машина-анализатор – системные библиотеки – драйвера – железо. Самым медленно работающим блоком в данной схеме является анализатор – он должен байт-код программы транслировать «на лету» в машинный код, при этом потратив много времени на точное определение инструкции. Поэтому быстрая разработка зачастую означает медленное выполнение.
4. Ориентированность на компьютер или человека. С кем будет работать программа в первую очередь? С человеком, или с компьютером? В пером случае программа должна обладать мощной графической частью, отвечающей требованиям дизайна и юзабилити. Разработка графической части зачастую требует достаточно много времени, так как отличается немалой сложностью. Здесь сложность возникает в том, что вывод графики – это много вычислений, а значит присутствует требовательность к скорости исполнения, а из-за сложности разработки присутствует необходимость в высокоуровневом языке. В данном случае очень хорошо подходит С++ и C# с их одновременной и высокоуровневостью, и скоростью выполнения программ на них. Однако, если графический интерфейс программы не очень сложный, но красивый, возможно использование Java и Flash, на которых создание красивых интерфейс гораздо проще, нежели на С++ и, тем более, С. Если программа ориентирована в первую очередь на «скрытую работу» с минимумом взаимодействия с пользователем, тогда выбор должен ложиться в сторону быстрых языков (ASM, C)
5. Кроссплатформенность. Под кроссплатформенностью понимается возможность работы программы на различных платформах, в различных оперативных системах с минимальными изменениями. В этой сфере можно выделить такие языки как Java, C#, Flash, C++ с различными библиотеками и другие, менее используемые, языки.
Java создавался с тем условием, что программы на данном языке должны работать на любой платформе, где есть JVM – Java Virtual Machine. Программы на Java вообще не требуют никаких изменений – после компиляции получается .jar файл, который будет работать и на Windows, и на Mac OS, и на Linux и еще немало где. Аналогичная ситуация и с Flash, только список платформ гораздо менее обширный. С языком С++ дело обстоит труднее. На чистом С++ написать кроссплатформенную программу довольно трудно, у кода возникает обширная избыточность, теряется достоинство в скорости выполнения. Облегчают задачу кроссплатформенные библиотеки, например, Qt, которые позволяют добиться принципа «один код на все платформы», однако на каждую платформу нужно программу собирать отдельно (при этом разными компиляторами).
В этот раздел так же можно включить интерпретируемые, скриптовые языки – для их работы нужно наличие интерпретатора языка в системе. Данные языки очень удобны в плане разработки, но достаточно медлительны. Схема их работы напоминает схему работы Java/Flash, только анализатор стал еще медленнее – полумашинный байт код анализировать «на лету» гораздо проще, чем человеческий код. Так же, это влечет к большему потреблению памяти.