Интерпретированный против скомпилированный: полезное различие?

29

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

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

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

Поэтому я спрашиваю: есть ли полезное различие между интерпретируемыми реализациями и скомпилированной реализацией в наши дни?

Уинстон Эверт
источник
7
@DeadMG Не так нова, как вы думаете: краткая история о том, как вовремя ...
Яннис
4
@DeadMG Учитывая, что большинство новых языков, представленных за последние 10 лет или около того, в основном работают на какой-то виртуальной машине, я бы сказал, что у него есть смысл. Конечно, все еще существуют (и будут на протяжении десятилетий) языки, скомпилированные с нативным кодом, и JIT останется роскошью (или нет, если парни из PyPy по-своему). Так что да, возможно завышение, но я согласен с тем, что основным направлением (на данный момент и в обозримом будущем), по-видимому, является компилятор байт-кода + возможно, JIT.
4
@DeadMG, у вас должна быть длинная белая борода, если модель виртуальной машины «новая» для вас. P-codeбыл введен в 1966 году первым. IBM Aix существует с 1986 года.
SK-logic
6
Такие вещи, как оболочки Unix, Tcl и другие, всегда будут интерпретироваться чисто, поэтому различие имеет смысл, по крайней мере, в академической среде. Но это правда, что когда программисты бормочут о интерпретаторах и компиляторах, они не имеют никакого смысла в большинстве случаев.
SK-logic
3
@ SK-logic, я думаю, что ваш комментарий - лучший ответ, чем любой из фактически опубликованных ответов
Уинстон Эверт

Ответы:

23

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

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

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

Зак Смит
источник
3
Да, программист должен иметь базовое понимание. Но мне интересно, не мешает ли скомпилированная / интерпретированная терминология.
Уинстон Эверт
2
Спасибо!! Интерпретация - это просто синоним слова «выполнено», и так запускаются все программы.
садовник
9

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

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

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

Интерпретация всегда будет полезна в качестве метода начальной загрузки компилятора. Для конкретного примера посмотрите на CLISP (популярная реализация Common Lisp). CLISP имеет компилятор, который написан сам по себе. Когда вы собираете CLISP, этот компилятор интерпретируется на ранних этапах сборки. Он используется для компиляции, а затем после компиляции выполняется компиляция.

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

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

Интерпретация: важная ступенька на пути, ведущем к компиляции!

Kaz
источник
1
ИМО, это лучший ответ. Я работаю над своим собственным игрушечным языком, и последний абзац описывает, как я его развиваю. Это действительно облегчает работу над новыми идеями. Также +1 за упоминание процесса начальной загрузки CLISP.
Синан
Теоретически, любой «интерпретируемый» язык может быть превращен в «скомпилированный» путем генерации EXE-файла, состоящего из интерпретатора плюс исходный код или байт-код для интерпретируемой программы. Хотя это может быть не очень эффективно.
Ден04
Узнайте, как Вирт и др. Изобрели P-код для упрощения переноса PASCAL на новые архитектуры машин. Это было в начале 1970-х годов.
Джон Р. Штром
1
Я подозреваю, что ваш первый абзац смешивает компиляцию и интерпретацию со статическим и динамическим поведением, но я дам вам преимущество сомнения и просто попрошу пример языка с семантикой, которую «практически невозможно» скомпилировать. Что касается начальной загрузки компилятора, это правда, что первая реализация должна быть написана не на том языке, который вы реализуете, а на интерпретаторе, это может быть компилятор, написанный на другом языке.
8bittree
1

На самом деле многие реализации языков все еще строго интерпретируются, вы просто можете не знать о них. Вот некоторые из них: языки оболочки UNIX, оболочки Windows cmd и PowerScript, Perl, awk, sed, MATLAB, Mathematica и так далее.

Чарльз Э. Грант
источник
3
Я считаю, что Perl внутренне скомпилирован в байт-код, и, по крайней мере, Mathematica может быть скомпилирована. И ничто не диктует реализацию awk и sed (я полагаю, что некоторые из GNU coreutils компилируют регулярные выражения в конечные автоматы перед выполнением, что, вероятно, помещало бы их в категорию «скомпилировать в промежуточное представление, интерпретировать эту»).
1
На самом деле я уверен, что Perl, MATLAB, Mathematica все компилируются в байт-код. Я не знаком с PowerScript, вы имеете в виду Powershell? Если это так, то при использовании CLR и так используется байт-код.
Уинстон Эверт
@WinstonEwert, извините, я имел в виду PowerShell. Насколько я понимаю, перевод в промежуточную форму не означает, что что-то не интерпретируется. Черт, оригинальный переводчик Dartmouth BASIC перед переводом переводил источник в токены. Каждый из упомянутых мной инструментов имеет режим, в котором он: 1) читает строку исходного кода, 2) переводит эту строку в исполняемую форму (возможно, некоторый промежуточный код, а не нативный код), 3) выполняет код для этой строки, 4) вернитесь к 1). Это соответствует моему пониманию переводчика.
Чарльз И. Грант
2
Байт-код подразумевает компилирование. Компилятор байт-кода - это просто программа, которая берет исходный код и преобразует его в байт-код. Следовательно, любое использование байт-кода должно включать компилятор байт-кода. Но байт-код также должен быть интерпретирован (или JITted). Таким образом, все, что использует байт-код, является гибридом интерпретатора / компилятора.
Уинстон Эверт
4
На самом деле, моя вещь в том, что люди выбрасывают такие утверждения, как «интерпретируется python» и «Java компилируется» без понимания реализаций. Я задаюсь вопросом, полезно ли даже описывать реализацию в этих терминах. Правда обычно более сложна, и попытка свести ее к интерпретации / компиляции бесполезна.
Уинстон Эверт
1

Я думаю: Абсолютно Да .

На самом деле, большинство реализаций сходятся на одной базовой стратегии

Действительно, C ++ стремится портировать в домен компилятора некоторую концепцию высокого уровня, которая обычно передается интерпретаторам, но остается на стороне меньшинства ...

CapelliC
источник
2
Подождите, пока Clang + LLVM не станет самым популярным компилятором.
SK-logic
@ SK-logic, несмотря на название, я считаю, что Clang + LLVM производит нативный код.
Уинстон Эверт
1
@ Уинстон Эверт, только если хочешь. Вы можете остановиться на уровне LLVM IR и делать с ним все что угодно - интерпретировать его, JIT-компилировать, инструментировать так, как вам нравится. Вы даже можете перевести его на Javascript, а затем передать через переводчика: github.com/kripken/emscripten/wiki
SK-logic
@ SK-логика, аккуратные вещи! Не знал, что LLVM может сделать это.
Уинстон Эверт
1
Прелесть llvm в том, что это преднамеренное разделение между передней частью и задней частью. И инструменты для манипулирования серединой перед нацеливанием на набор инструкций. Вы можете объединить весь ваш проект в байт-код, а затем оптимизировать по всему объекту, с другими компиляторами вам потребуется один источник файла или хотя бы один, включающий его путь вниз по дереву исходного кода, чтобы компилятор работал с одним объединенным источником. Также один набор инструментов под llvm является общим для всех целей, вам не нужно создавать для каждой цели, один компилятор подходит для всех (по крайней мере, на уровне цели).
old_timer
-1

Полезное различие: интерпретируемые программы могут изменять себя, добавляя или изменяя функции во время выполнения.

Диего Пачеко
источник
8
Ерунда. Самомодифицирующийся (машинный) код - самая старая уловка в книге. С другой стороны, некоторые утверждают, что даже нативный код в конечном итоге интерпретируется интерпретатором, преобразованным в кремний (процессор). Но если мы предположим, что весь код интерпретируется, и нет никаких различий.
2
@Делнан прав. Я просто добавлю, что современные языки могут модифицировать себя, создавая новые классы динамически и загружая / выгружая библиотеки (или, например, «сборки» в .NET)
Jalayn
5
Common Lisp компилируется, но вы все равно можете легко заменить определения функций во время выполнения.
SK-logic
Это действительно интересная и необходимая (например, в Прологе) особенность интерпретации.
CapelliC