Чем отличаются компилируемые и интерпретируемые языки программирования? Список языков программирования. Языки программирования низкого и высокого уровня

14.07.2019


Notice : Функция get_currentuserinfo с версии 4.5.0 считается устаревшей ! Используйте wp_get_current_user(). in /hlds/web/u138079p19/сайт/htdocs/wp-includes/functions.php on line 3840

Компилируемые и интерпретируемые языки, казалось бы что здесь не так? Компилируемые компилируются в исполняемый код, а интерпретируемые интерпретируются на этапе исполнения. Но не все так однозадачный. Неоднократно при общении с программистами слышал путаницу в терминологии по поводу компилируемых и интерпретируемых языков программирования. Объяснения не всегда логичны и уж тем более объективны (и эта статья возможно тоже).

И вот перед выпуском s4g пообщавшись с товарищем … решил поставить под сомнение свое понимание этих понятий и безотносительно выяснить что это все значит, и как оказалось совсем не зря, потом что даже вики говорит о размытости границ между этими понятиями:

Классификация языков программирования на компилируемые и интерпретируемые является неточной и весьма условной. Существуют реализации языков, которые компилируют исходный текст программы в байт-код, который затем либо интерпретируется , либо выполняется JIT-компилятором или виртуальной машиной . Это привносит ещё больше неясности в вопрос о том, где именно должна быть проведена граница.

Примечание: данная статья имеет анализ, но это не значит что он абсолютно верный, возможно где-то я субъективно интерпретировал понятия))

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

Теперь необходимо понять что такое компиляция, опять обращаемся к вики :

Компиляция - трансляция программы, составленной на исходном языке высокого уровня, в эквивалентную программу на низкоуровневом языке, близком машинному коду.

Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке, подлежащем интерпретации виртуальной машиной. Такой язык называется псевдокодом или байт-кодом .

То есть компиляция может осуществляться и в байт код для конкретной виртуальной машины, и в таком случае любой скриптовый язык, которые компилируется в байт-код может считаться компилируемым .

Разберем теперь понятие интерпретация, опять же обращаемся к вики :

Интерпретация (в математике, логике) - совокупность значений (смыслов), придаваемых тем или иным способом элементам (выражениям, формулам, символам и т. д.) какой-либо естественнонаучной или абстрактно-дедуктивной теории.

Интерпретация исходного кода - пооператорный (покомандный, построчный) анализ, обработка и тут же выполнение исходной программы или запроса (в отличие от компиляции, при которой программа транслируется без её выполнения).

Казалось бы … теперь все понятно, но сомнения все-таки остались. Ну а как же тогда точно расставить все по местам? Ведь даже при таком раскладе получается что практически для любого языка можно написать интерпретатор?

Процесс компиляции состоит из следующих этапов:

  1. Лексический анализ

  2. Синтаксический анализ

  3. Генерация кода

Все-равно мало …

Обратимся еще раз к понятию интерпретация : совокупность значений, придаваемых тем или иным способом элементам какой-либо естественнонаучной или абстрактно-дедуктивной теории. Значит ли это что интерпретация это одна итерация?

Разберем процесс компиляции любого языка у которого есть хотя бы лексический анализ, синтаксический и генератор кода (не важно машинный или байт код, главное есть наличие генератора кода):

  • сначала на вход поступает поток символов (исходный код), который разбивается на лексемы, то есть первая итерация интерпретации — делаем понятным поток символом для синтаксического анализатора (интерпретируем для парсера);
  • на этапе синтаксического анализа строим абстрактное синтаксическое дерево (АСТ), то есть вторая итерация — делаем понятными лексемы (не исходный код) для генератора кода (интерпретируем для генератора кода);
  • на этапе генерации кода преобразуем АСТ в код (машинный либо байт-код), то есть третья итерация — делаем понятным АСТ (не лексемы и уж тем более не исходный код) для машины (реальной либо виртуальной) (интерпретация для машины), итог — код.

Получая на вход исходный код , пропуская его через 3 фильтра (лексический анализатор, синтаксический анализатор, генератор кода) получается на выходе готовый к исполнению код . А вот какой код, машинный, либо байт-код это уже совсем другое дело, и нам абсолютно безразлично кто его будет исполнять, реальная либо виртуальная машина, самое главное что получился код.

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

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

Ладно, определились, но что тогда в таком случае будет являться интерпретируемым языком программирования? Судя по всему, таковым языком будет тот язык, который имеет только одну итерацию интерпретации . Утверждать не буду, но ИМХО, такой язык лишен высокоуровневости, и будет представлять из себя нечто вроде:

Push 10 push 5 add

То есть некое подобие ассемблера, похожего на байт-код виртуальной машины))

В этом случае нет необходимости лексического и синтаксического анализа, и генерировать код вовсе не обязательно, его можно сразу же интерпретировать виртуальной машиной, к примеру.

Часто скриптовые языки называют интерпретируемыми еще и потому что генератор кода в них называется компилятором, это конечно же не верно, и мы сами неверно назвали в s4g 0.9.2 генератор кода компилятором. Компилятор это нечто большее чем простая интерпретация АСТ в байт-код. В следующих версиях будем исправляться))

Также можно услышать аргумент о том что виртуальная машина интерпретирует байт-код, поэтому язык в котором есть виртуальная машина будет интерпретируемым. В таком случае центральный процессор тоже интерпретирует машинный код. И второе утверждение настолько же верно насколько верно первое))

Если уж компилируемый язык компилируется в машинный код … тогда он будет исполнятся как на Windows так и на Linux с аналогичным железом?))

Итак, запрепим:

Интепретатор — программа исполняющая исходный код пооператорно (построчно, по командно).

Интерпретируемый язык программирования — это язык программирования имеющий один уровень итерации (восприятия) исходного кода. Промежуточное представление кода отсутствует.

Компилируемый язык программирования — это язык программирования имеющий несколько преобразований от начала поступления исходного кода и до генерации исполняющего кода включительно, имеет несколько промежуточных состояний представления исходного кода.

Языки программирования делятся на три типа:

1. Комплируемые языки

Работу компилируемых языков можно представить следующей схемой:

То есть жизненный цикл программы представляет собой следующие этапы:
1. Написание исходного текста программы (source code)
2. Компиляция в исполнимый файл (.exe например)
3. Выполнение программы на ПК

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

Примеры таких языков: assembler, C, C++, Pascal

2. Интерпретируемые языки

Соответственно, жизненный цикл программы сводится к:
1. Написание
2. Выполнение

По сути интерпретируемые программы это так называемые "скрипты" - описание набора действий которые должен выполнить интерпретатор.
То есть, интерпретатор это большая программа со множеством различных функций. А программа это указание какие функции в каком порядке вызвать, описание взаимодействия этих функций

Это специфические языки, например языки для создания веб-страниц.
Примеры: PHP, JavaScript, Python

3. Смешанного типа

Принцип работы языков смешанного типа (компилируемо-интерпретируемые) проиллюстрируем такой схемой:


Java относится именно к компилируемо-интерпретируемым языкам программирования. Интерпретатор в Java называется "виртуальная машина", именно поэтому он так обозначен на рисунке.

Возникает вопрос, зачем такая сложность? Дело в том что такой подход объединяет преимущества компилируемых языков (скорость выполнения) и интерпретируемых (независимость от ОС и безопасность)

Примеры таких языков: Java, C#

Эти три подхода можно проиллюстрировать следующим образом:

1. Компилируемые языки это готовая еда. Хорошо иметь ее под рукой, но она быстро портится.
Программы, написанные на компилируемых языках зависят от ОС и от аппаратного оборудования

2. Интерпретируемые языки это замороженные продукты. Они хорошо хранятся, но их долго готовить
Программы, написанные для интерпретируемых языков очень мобильны, работают везде, где есть интерпретатор, безопасны, но...
Процесс преобразования занимает время, что сказывается на производительности

3. Комбинированные языки можно сравнить с полуфабрикатом. Его быстро готовить, он долго хранится. Но как вы понимаете, вкус будет хуже, чем у свежеприготовленной пищи.
Программы написанные на комбинированных языках программирования, ограничены функциональностью своего интерпретатора. То есть возможностей которые может использовать такая программа всегда будет меньше чем возможностей, которые может использовать программа написанная на компилируемом языке

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

Целью компиляции является реализация возможности выполнения реальным компьютером кода, написанного для абстрактной машины. Компиляция , однако, – лишь одна из двух базисных технологий, применяемых для достижения нашей цели.

Базисные схемы

Вместо компиляции программы можно ее интерпретировать. Рисунок ниже иллюстрирует разницу подходов, игнорируя роль входных данных.


Рис. 3.6.

И компилятор, и интерпретатор являются программами, на вход которых подаются программы, написанные на языке программирования. Компилятор транслирует переданную ему программу в целевую форму – машинный код, который уже может быть непосредственно выполнен на компьютере, создавая выходные результаты вычислений. Обработка той же исходной программы интерпретатором не создает целевой программы, но непосредственно выполняет ее, получая выходные результаты.

Интерпретатор должен быть способен определить эффект выполнения каждой конструкции языка программирования. Как пример того, как интерпретатор выполняет свою задачу, рассмотрим интерпретацию присваивания x: = x +1 . Интерпретатор должен хранить таблицу всех используемых переменных и связанных с ними значений. Он вычисляет новое значение x , добавляя 1 к старому значению, хранящемуся в таблице, а затем выполняет присваивание, заменяя старое значение x значением вычисленного выражения.

Компилятор будет генерировать машинный код, создающий тот же эффект, используя команды компьютера и адреса памяти (а не структуру более высокого уровня, такую как таблица).

В дополнительном упражнении для небольшого языка потребуется, применяя эти идеи, написать компилятор и интерпретатор.

Если рассматривать язык программирования как машинный код для абстрактной машины, можно сказать, что интерпретатор – это программа, моделирующая вычисление на этой машине. Машинная память в этом случае также абстрактна и представляет структуры данных в виде таблиц интерпретатора "переменные – значения".

На следующем рисунке процессы компиляции и интерпретации дополнены вводом данных:


Рис. 3.7.

Рисунок демонстрирует еще одну разницу между компилятором и интерпретатором. У интерпретатора два источника ввода – исходная программа и входные данные; а компилятору подается только программа. В последующем обсуждении этому различию придадим математическую форму.

Компилировать или интерпретировать? Эта проблема – предмет широко рассмотрения в компьютерных науках. Что лучше: непосредственно обрабатывать исходную информацию в том виде, как она есть, или предварительно привести ее к более удобной форме? Этот вопрос стоит не только при обработке программ, мы будем сталкиваться с ним и при изучении алгоритмов.

У компиляторов и интерпретаторов имеются свои достоинства. Возможны различные критерии. По производительности – времени выполнения программы – компиляторы побеждают.

  • Выход компилятора является машинным кодом, непосредственно выполняемым компьютером. Дополнительно при создании этого кода компилятор мог применять оптимизацию, улучшающую эффективность кода.
  • Интерпретация кода требует при выполнении каждого оператора его предварительной обработки. В результате интерпретация программы выполняется на порядок медленнее в сравнении с работой программы, созданной компилятором.

Все меняется, если в качестве критерия выбрать удобство и скорость разработки. Компилятор стоит между вами и реализацией вашей последней идеи: прежде чем увидеть результаты последнего изменения в программе, необходимо ждать результата компиляции (и связывания, о чем ниже пойдет речь). При интерпретации выполнение начинается незамедлительно.

В современных средах разработки этот недостаток компиляции не является столь критическим благодаря применяемой технологии "возрастающей компиляции" , когда при внесении изменений компилируются только те части программы, на которых это изменение сказывается. В конце этой лекции мы рассмотрим, как эта технология работает в EiffelStudio.

Еще один критерий, согласно которому предпочтение опять-таки отдается компиляции, состоит в надежности программы. Компиляторы не просто транслируют программу – в процессе компиляции они осуществляют различные проверки, например, контроль типов для статически типизированных языков . Тем самым многие ошибки устраняются еще на этапе компиляции, в то время как для режима интерпретации ошибки обнаруживаются в процессе выполнения на одном из сеансов работы.

В принципе, интерпретатор также способен выполнять некоторые из проверок перед выполнением программы. Фактически, чистые интерпретаторы не применяются, они представляют собой всегда некоторую смесь компиляции и интерпретации.

Комбинирование компиляции и интерпретации

Схемы чистой компиляции и чистой интерпретации являются предельными вариантами: большинство практических решений является смесью. Это верно и для процесса компиляции в EiffelStudio, который будет рассмотрен позже в этой лекции.

Заметим, что 100% схема интерпретации имеет мало смысла: каждый раз, когда интерпретатор выполнял очередной оператор, например, оператор цикла, он должен был бы возвращаться многократно к фактической последовательности символов и осуществлять ее разбор. Любое реалистическое решение не могло бы согласиться с такой неразумной тратой ресурсов. Так что фактически интерпретатор также начинает с преобразования входа в форму, приемлемую для интерпретации, например, строя абстрактное синтаксическое дерево. В ходе этого процесса, как отмечалось, возможен контроль проверки типов. Так что даже тогда, когда можно прочесть, что используется интерпретатор языка, частичная компиляция подразумевается.

Комбинирование интерпретации и компиляции идет значительно дальше рассмотренной основной идеи. Выход компилятора не должен быть непосредственным машинным кодом, он может быть субъектом дальнейшего процесса обработки.


Рис. 3.8.

Смешанная стратегия предполагает, что компилятор создает код на промежуточном языке , понимаемом некоторой виртуальной машиной – VM на рисунке. Такой подход объединяет преимущества компиляции и интерпретации. Благодаря тщательно спроектированной виртуальной машине возможно получить:

  • переносимость, так как VM-код не зависит от специфики физических процессоров;
  • повышение эффективности, поскольку создаваемый промежуточный код легко интерпретируется.

Виртуальные машины, байт-код и JIT (Just In Time) компиляторы

Реализация современных языков – Java, C#, других языков.Net – основана на смешанном решении. Промежуточный код для Java называется байт-кодом. В термине отражается тот факт, что виртуальная машина использует компактные команды, подобные командам фактического процессора, где каждая команда содержит код команды – типично задаваемый одним байтом, – после которого следует 0, 1 или 2 аргумента команды.

Альтернативой байт-коду могла бы выступать виртуальная машина, непосредственно работающая со структурами данных, например, с абстрактным синтаксическим деревом для представления структуры программы и с хэш-таблицами для хранения свойств переменных. Но байт-код обеспечивает лучшую эффективность периода выполнения, как по времени, так и по памяти.

Прием двухэтапной компиляции был использован еще в семидесятые годы при реализации компилятора с языка Паскаль. Он получил второе рождение с распространением Интернета, так как хорошо был приспособлен для локального выполнения Web-клиентами. Поставщики апплетов – небольших программ – могли компилировать их в байт-код и поставлять их в такой форме. Дополнительным преимуществом к компактности стала переносимость кода, поскольку в противном случае машинный код пришлось бы создавать для каждой возможной целевой платформы .

Для выполнения апплета пользователям необходим только интерпретатор байт-кода. Они даже не должны знать, что такой интерпретатор существует, если он встроен в их Web-браузер, Поскольку при таком подходе возникают потенциальные риски, связанные с безопасностью, – жульнические или некорректные апплеты могут повредить ваш компьютер, – по этой причине для апплетов необходим интепретатор, который будет строго контролировать операции, разрешенные для апплетов.

Поставка программ через апплеты достигла некоторого успеха, но не стала основным способом распределенного ПО, как ожидалось в момент появления Java. Частично это связано с проблемами безопасности, но главная причина – в потере эффективности, возникающей по причине интерпретации. Большинство успешных апплетов являются небольшими программами, предназначенными для выполнения на Web-странице, включающие визуальную компоненту, при наличии которой потери времени представляются несущественными.

Для улучшения эффективности времени выполнения байт-кода применяются JIT (Just In Time) компиляторы, называемые джитерами, – осуществляющие компиляцию по требованию. Основная идея состоит в том, что машинный код для некоторого модуля создается "на лету", в тот момент, когда он первый раз вызывается на выполнение (не следует путать любителя джаза –jitterbug, с ошибками такого компилятора – jitter bug ). Внесем соответствующие дополнения в предыдущий рисунок, который теперь выглядит так:


Рис. 3.9.

Обычно, как показано на рисунке, наряду с компиляцией "на лету" (джитингом) остается и возможность интерпретации байт-кода. Компиляция "на лету" обычно имеет место при первом использовании модуля (метода или всего класса), так что она будет нужна только для кода, фактически используемого в этом сеансе выполнения. В сравнении с традиционным компилятором, который компилирует всю программу, такой подход позволяет создавать более компактный код, сокращает время компиляции, но, что более важно, делает компиляцию частью процесса выполнения. Последнее является серьезным недостатком, поскольку к времени выполнения добавляются расходы на компиляцию, так что само время выполнения становится менее предсказуемым.

С первого взгляда кажется, что при таком подходе не стоит выполнять проверки типов и другой контроль, поскольку кому же хочется во время выполнения получать сообщения о нарушении согласованности типов ? Это возвращало бы нас к проблемам динамически типизированных языков . Конечно, нам хотелось бы, чтобы все необходимые проверки выполнялись на первом шаге компиляции при создании байт-кода, так, чтобы любой код, передаваемый джитеру, был безопасным. К сожалению, эти утешительные предположения нереалистичны в распределенной среде, где опять возникают проблемы безопасности. Если вы загружаете байт-код из сайта, то можете ли вы знать, прошел ли он проверку? В общем случае – нет. Но тогда нарушения типа могут стать не только причиной нарушения надежности и аварийного завершения программы, все может быть гораздо хуже: в результате атаки становится возможным нарушение безопасности.

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

Как следствие, на практике компиляция на лету включает в любом случае проверку согласованности типов . Потери производительности при этом могут оставаться приемлемыми, поскольку система типов виртуальной машины с байт-кодом значительно проще, как правило, чем система типов исходной программы.

Стратегия компиляции в EiffelStudio также включает байт-код, но, как мы увидим, она использует различные способы комбинирования интерпретации и компиляции.

В общем случае, любой язык может быть компилируемым и интерпретируемым, так что данное разделение относится к практике применения языка, а не является его свойством. При этом для многих языков существует различие в производительности между компилируемой и интерпретируемой реализацией.

Большое количество языков, включая BASIC , , Lisp , Pascal и Python , имеют обе реализации. В Java используется JIT-компиляция для генерации машинного кода, хотя изначально он переводится в интерпретируемую форму. Языки Microsoft .NET Framework компилируются в Common Intermediate Language (CIL) , который во время выполнения компилируется в нативный код. Большинство реализаций Lisp позволяют смешивать оба вида кода.

История

В ранние годы развития программирования на языки сильно влиял выбор способа выполнения. Например, компилируемые языки требовали задания типа данных переменной в момент ее описания или первого использования. В то время как интерпретируемые языки в силу своей динамической природы позволяли отказаться от этого требования, что давало больше гибкости и ускоряло разработку.

Изначально интерпретируемые языки преобразовывались в машинный код построчно, то есть каждая логическая строка компилировалась непосредственно перед выполнением. В результате каждая инструкция, заключенная в тело цикла и исполняемая несколько раз, столько же раз обрабатывалась транслятором. В настоящее время такие эффекты редки. Большинство интерпретируемых языков предварительно транслируются в промежуточное представление. Оно представляет собой байт-код или шитый код (threaded code) . Это набор инструкций по вызову небольших фрагментов более низкоуровнего кода, эквивалентный нескольким командам ассемблера или командам виртуальной машины соответственно. Уже этот код испольняется интерпретатором или виртуальной машиной.

Например, такую схему используют следующие языки:

  • Ruby (использует представление кода в виде абстрактного синтаксического дерева)

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

Преимущества

Есть ряд возможностей, которые значительно легче реализовать в интерпретаторе, чем в компиляторе:

  • использование динамической области видимости и замыканий

Кроме того, принципы и стиль программирования часто не требуют создания и описания специальных конструкций, оформляющих программу (манифестов, классов, типов данных). Это позволяет разрабатывать и тестировать код постепенно, что удобно как для написания небольших программ, так и для изолированной разработки модулей для сложных систем. В силу своей универсальности их удобно применять в качестве скриптовых языков .

Недостатки

Основным недостатком является меньшая производительность по сравнению с компилируемыми языками, преобразуемыми в машинный код . Трансляция в байт-код и JIT-компиляция не решают этой проблемы полностью. Дополнительный слой интерпретатора или виртуальной машины замедляет выполнение программы и может требовать больше ресурсов.

Из-за отсутствия компиляции всего проекта большинство ошибок можно поймать только во время выполнения. В результате в среднем интерпретируемый код следует тестировать тщательнее компилируемого, строже придерживаться соглашений по оформлению программ и использовать дополнительные анализаторы качества кода. Последний недостаток выражен несильно, так как при серьезной разработке на компилируемых языках также необходимо применение этих средств.

Области использования

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

Пример


Wikimedia Foundation . 2010 .

Смотреть что такое "Интерпретируемый язык программирования" в других словарях:

    Разработанный корпорацией Sun Microsistems межплатформенный, интерпретируемый, объектно ориентированный язык программирования, используемый для создания приложений, работающих в под управлением веб браузера и характеризующихся высоким уровнем… … Финансовый словарь

    Ruby Семантика: мультипарадигмальный Тип исполнения: интерпретатор Появился в: 1995 г. Автор(ы): Юкихиро Мацумото Последняя версия: 1.9.1 … Википедия

    Язык программирования формальная знаковая система, предназначенная для записи компьютерных программ. Язык программирования определяет набор лексических, синтаксических и семантических правил, задающих внешний вид программы и действия,… … Википедия

    У этого термина существуют и другие значения, см. B. У этого термина существуют и другие значения, см. БИ (значения). B Класс языка: процедурная Тип исполнения: интерпретируемый Появился в: 1969 А … Википедия

    У этого термина существуют и другие значения, см. Диалект (значения). Диалект Тип исполнения: интерпретируемый Автор(ы): Джордж Харт Диалект интерпретируемый язык программирования, созданный Джорджем Хартом в рамках институтского проекта по … Википедия Википедия

    ABC Класс языка: императивный, процедурный, структурный Тип исполнения: интерпретируемый, компилируемый Появился в: 1987 Автор(ы): Leo Geurts, Lambert Meertens (… Википедия

Существует несколько подходов к определению семантики языков программирования.

Наиболее широко распространены разновидности следующих трёх: операционного, денотационного (математического) и деривационного (аксиоматического).

При описании семантики в рамках операционного подхода обычно исполнение конструкций языка программирования интерпретируется с помощью некоторой воображаемой (абстрактной) ЭВМ.

Деривационная семантика описывает последствия выполнения конструкций языка с помощью языка логики и задания пред- и постусловий. Денотационная семантика оперирует понятиями, типичными для математики -- множества, соответствия, а также суждения, утверждения и др.

Язык программирования строится в соответствии с той или иной базовой моделью вычислений и парадигмой программирования.

Несмотря на то, что большинство языков ориентировано на императивную модель вычислений, задаваемую фоннеймановской архитектурой ЭВМ, существуют и другие подходы. Можно упомянуть языки со стековой вычислительной моделью (Forth, Factor, Postscript и др.), а также функциональное (Лисп, Haskell, ML и др.) и логическое программирование (Пролог) и язык Рефал, основанный на модели вычислений, введённой советским математиком А.А. Марковым-младшим.

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

Компилируемые и интерпретируемые языки

Языки программирования могут быть разделены на компилируемые и интерпретируемые.

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

Если программа написана на интерпретируемом языке, то интерпретатор непосредственно выполняет (интерпретирует) исходный текст без предварительного перевода. При этом программа остаётся на исходном языке и не может быть запущена без интерпретатора. Можно сказать, что процессор компьютера -- это интерпретатор машинного кода.

Кратко говоря, компилятор переводит исходный текст программы на машинный язык сразу и целиком, создавая при этом отдельную исполняемую программу, а интерпретатор выполняет исходный текст прямо во время исполнения программы.

Разделение на компилируемые и интерпретируемые языки является несколько условным. Так, для любого традиционно компилируемого языка, как, например, Паскаль, можно написать интерпретатор. Кроме того, большинство современных «чистых» интерпретаторов не исполняют конструкции языка непосредственно, а компилируют их в некоторое высокоуровневое промежуточное представление (например, с разыменованием переменных и раскрытием макросов).

Для любого интерпретируемого языка можно создать компилятор -- например, язык Лисп, изначально интерпретируемый, может компилироваться без каких бы то ни было ограничений. Создаваемый во время исполнения программы код может так же динамически компилироваться во время исполнения.

Как правило, скомпилированные программы выполняются быстрее и не требуют для выполнения дополнительных программ, так как уже переведены на машинный язык. Вместе с тем, при каждом изменении текста программы требуется её перекомпиляция, что создаёт трудности при разработке. Кроме того, скомпилированная программа может выполняться только на том же типе компьютеров и, как правило, под той же операционной системой, на которую был рассчитан компилятор. Чтобы создать исполняемый файл для машины другого типа, требуется новая компиляция.

Интерпретируемые языки обладают некоторыми специфическими дополнительными возможностями (см. выше), кроме того, программы на них можно запускать сразу же после изменения, что облегчает разработку. Программа на интерпретируемом языке может быть зачастую запущена на разных типах машин и операционных систем без дополнительных усилий.

Однако интерпретируемые программы выполняются заметно медленнее, чем компилируемые, кроме того, они не могут выполняться без дополнительной программы-интерпретатора.

Некоторые языки, например, Java и C#, находятся между компилируемыми и интерпретируемыми. А именно, программа компилируется не в машинный язык, а в машинно-независимый код низкого уровня, байт-код. Далее байт-код выполняется виртуальной машиной. Для выполнения байт-кода обычно используется интерпретация, хотя отдельные его части для ускорения работы программы могут быть транслированы в машинный код непосредственно во время выполнения программы по технологии компиляции «на лету» (Just-in-time compilation, JIT). Для Java байт-код исполняется виртуальной машиной Java (Java Virtual Machine, JVM), для C# -- Common Language Runtime.

Подобный подход в некотором смысле позволяет использовать плюсы как интерпретаторов, так и компиляторов. Следует упомянуть также оригинальный язык Форт(Forth) имеющий и интерпретатор и компилятор.

Современные языки программирования рассчитаны на использование ASCII, то есть доступность всех графических символов ASCII является необходимым и достаточным условием для записи любых конструкций языка. Управляющие символы ASCII используются ограниченно: допускаются только возврат каретки CR, перевод строки LF и горизонтальная табуляция HT (иногда также вертикальная табуляция VT и переход к следующей странице FF).

Подробнее по этой теме см.: Переносимый набор символов.

Ранние языки, возникшие в эпоху 6-битных символов, использовали более ограниченный набор. Например, алфавит Фортрана включает 49 символов (включая пробел): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 = + - * / () . , $ " :

Заметным исключением является язык APL, в котором используется очень много специальных символов.

Использование символов за пределами ASCII (например, символов KOI8-R или символов Юникода) зависит от реализации: иногда они разрешаются только в комментариях и символьных/строковых константах, а иногда и в идентификаторах. В СССР существовали языки, где все ключевые слова писались русскими буквами, но большую популярность подобные языки не завоевали (исключение составляет. Встроенный язык программирования 1С: Предприятие).

Подробнее по этой теме см.: Русские языки программирования.

Расширение набора используемых символов сдерживается тем, что многие проекты по разработке программного обеспечения являются международными. Очень сложно было бы работать с кодом, где имена одних переменных записаны русскими буквами, других -- арабскими, а третьих -- китайскими иероглифами. Вместе с тем, для работы с текстовыми данными языки программирования нового поколения (Delphi 2006, C#, Java) поддерживают Unicode.

Похожие статьи