Складено проти тлумачених мов


284

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

Більшість мого досвіду програмування було з CPython (динамічний, інтерпретований) та Java (статичний, складений). Однак я розумію, що є інші види інтерпретованих та складених мов. Крім того, що виконувані файли можуть поширюватися з програм, написаних на компільованих мовах, чи є якісь переваги / недоліки для кожного типу? Часто я чую, як люди стверджують, що інтерпретовані мови можна використовувати інтерактивно, але я вважаю, що компільовані мови можуть також мати інтерактивні реалізації, правильно?


32
Ви вибрали саме найгірші мови для цього порівняння. Обидва є байткомпільованими. Єдина реальна різниця між ними - JITer, і навіть у Python є часткова (psyco).
Ігнасіо Васкес-Абрамс

1
Хорошим прикладом інтерактивної компільованої мови є Clojure - все повністю складено (спочатку до JVM, потім до рідного коду через JIT). Однак багато рекомпіляції відбувається динамічно, і розробка часто проводиться в інтерактивній оболонці REPL, де ви можете оцінити будь-яку функцію, яку ви хочете, в робочому середовищі.
mikera

Стандартний ML - ще одна інтерактивна компільована мова; вбудований компілятор також видає справжній натурний машинний код.
Стипендіати доналу


Відповіді:


459

Мова скомпільована - це та, де програма, щойно складається, виражається в інструкціях цільової машини. Наприклад, операція додавання "+" у вихідному коді може бути переведена безпосередньо в інструкцію "ADD" в машинному коді.

Інтерпретована мова є одним де інструкції безпосередньо не виконується на цільовій машині, але замість того, щоб прочитати і виконати який - або іншої програми (яка зазвичай є написаної на мові рідній машини). Наприклад, та сама операція "+" буде розпізнана інтерпретатором під час виконання, який потім називатиме власну функцію "add (a, b)" з відповідними аргументами, яка б потім виконала машинний код "ADD" .

Ви можете робити все, що ви можете зробити, на інтерпретованій мові складеною мовою, і навпаки - вони обидва Тюрінга завершені. Однак обидва мають переваги та недоліки для впровадження та використання.

Я збираюся повністю узагальнити (пуристи пробачте мене!), Але, приблизно, ось переваги складених мов:

  • Швидше виконання продуктивності безпосередньо за допомогою рідного коду цільової машини
  • Можливість застосувати досить потужні оптимізації під час етапу компіляції

І ось переваги інтерпретованих мов:

  • Простіше в реалізації (писати хороші компілятори дуже важко !!)
  • Не потрібно запускати етап компіляції: може виконувати код безпосередньо "на льоту"
  • Може бути зручнішим для динамічних мов

Зауважте, що сучасні методи, такі як компіляція байт-коду, додають певної складності - тут відбувається компілятор, спрямований на "віртуальну машину", яка не є такою ж, як на базовому обладнання. Ці інструкції щодо віртуальної машини потім можуть бути скомпільовані ще раз на більш пізньому етапі, щоб отримати власний код (наприклад, як це зроблено компілятором Java JVM JIT).


1
Не всі компільовані мови потребують повільного етапу компіляції. Серйозні реалізації Common Lisp - це компілятори, і вони часто не переймаються інтерпретатором, вважаючи за краще просто збирати реально швидко. З іншого боку, Java потрібен крок компіляції, і зазвичай це видно.
Девід Торнлі

2
@Kareem: компілятор JIT робить лише 1) і 2) один раз - після цього він є власним кодом. Інтерпретатору потрібно робити як 1), так і 2) кожен раз, коли викликається код (що може бути багато, багато разів ...). Тож з часом компілятор JIT виграє з великим відривом.
mikera

3
Так, байт-код переводиться на машинний код в якийсь момент під час виконання загальної програми (на відміну від виконання програми, як це відбувається у традиційному компіляторі). Але даний фрагмент коду може бути виконаний 10 мільйонів + разів під час загального виконання програми. Вона (ймовірно) збирається лише один раз з байт-коду до машинного коду. Отже, накладні витрати JIT невеликі, і їх можна ігнорувати для тривалих програм. Після того, як компілятор JIT закінчить виконувати свою роботу, ви ефективно будете виконувати чистий машинний код увесь час.
mikera

2
Це насправді помилкова дихотомія. Ніщо не властиве мові, що змушує її складати нашу інтерпретовану. Це не що інше, як широко поширене оману. Багато мов мають обидві реалізації, і всі мови можуть мати будь-яку.
mmachenry

2
@mmachenry це не хибна дихотомія. "мова програмування" включає в себе як розробку, так і реалізацію. Хоча в теоретичному сенсі дане визначення мови можна як складати, так і тлумачити, то в реальній світовій практиці існують значні відмінності в реалізації. Ніхто ще не вирішив, як ефективно складати певні мовні конструкції, наприклад - це відкрита дослідницька проблема.
mikera

99

Мова сама по собі ні складена, ні інтерпретована, є лише специфічна реалізація мови. Java - ідеальний приклад. Існує платформа на основі байт-коду (JVM), власний компілятор (gcj) та інтерпетер для суперсети Java (bsh). То яка зараз Java? Скомпільований байт-код, скомпільований нативним чи інтерпретованим?

Іншими мовами, які складаються та інтерпретуються, є Scala, Haskell або Ocaml. Кожна з цих мов має інтерактивний інтерпретатор, а також компілятор для байт-коду або власного машинного коду.

Тому класифікація мов за "складеними" та "інтерпретованими" взагалі не має великого сенсу.


3
Я згоден. Або скажімо: Є вбудовані компілятори (створюють машинний код для того, щоб центральний процесор їв), і не дуже рідні компілятори (створюють токенізовані речі, тобто проміжний код, який деякий своєчасний компілятор збирає до машинного коду раніше ( або під час) виконання ONCE), і є "справжні" некомпілятори, які ніколи не виробляють машинний код і ніколи не дозволяють ЦП виконувати код. Останні - перекладачі. Сьогодні рідні компілятори, які безпосередньо виробляють машинний (CPU) код у час компіляції, стають все рідшими. Delphi / Codegear - один з найкращих вцілілих.
TheBlastOne

57

Почніть думати з точки зору: вибуху з минулого

Колись, давно-давно, там жили в країні обчислювальні перекладачі та укладачі. Всілякі суєти, що виникають за достоїнства одного над іншим. Загальна думка в той час була чимось таким чином:

  • Перекладач: Швидкий розвиток (редагування та запуск). Повільно виконувати, тому що кожне твердження доводилося інтерпретувати в машинний код кожного разу, коли воно виконується (подумайте, що це означало для циклу, виконаного тисячі разів).
  • Компілятор: Повільно розвиватися (редагувати, компілювати, зв’язувати та запускати. Кроки компіляції / посилання можуть зайняти серйозний час). Швидке виконання. Вся програма вже була в кодовому машинному коді.

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

Сьогодні ландшафт розвинувся настільки, що складене / інтерпретоване розмежування є в значній мірі неактуальним. Багато мов компільованої служби запускають служби, що працюють не на основі машинного коду. Також більшість інтерпретованих мов "компілюються" в байт-код перед виконанням. Інтерпретатори байт-коду можуть бути дуже ефективними і конкурувати з деяким згенерованим компілятором кодом з точки зору швидкості виконання.

Класична відмінність полягає в тому, що компілятори генерують власний машинний код, інтерпретатори читають вихідний код і генерують машинний код на льоту, використовуючи якусь систему виконання часу. Сьогодні залишилося дуже мало класичних інтерпретаторів - майже всі вони компілюються у байт-код (або якийсь інший напівкомпільований стан), який потім працює на віртуальній "машині".


1
Ніцца - чудовий підсумок в останньому абзаці - дякую!
ckib16

26

Крайні і прості випадки:

  • Компілятор створить двійковий виконаний файл у вихідному форматі цільової машини. Цей двійковий файл містить усі необхідні ресурси, крім системних бібліотек; він готовий до запуску без подальшої підготовки та обробки, і він працює як блискавка, оскільки код є нативним кодом для процесора на цільовій машині.

  • Інтерпретатор подасть користувачеві підказку в циклі, куди він може вводити висловлювання або код, а після натискання RUNабо еквівалент інтерпретатор буде вивчати, сканувати, аналізувати та інтерпретативно виконувати кожен рядок, поки програма не запуститься до точки зупинки або помилки . Оскільки кожен рядок обробляється самостійно, а перекладач нічого не «вивчає» з того, що бачив лінію раніше, зусилля з перетворення читаної людиною мови в машинні інструкції докладаються щоразу для кожного рядка, тож це собака повільно. Зі сторони, користувач може перевіряти та іншим чином взаємодіяти зі своєю програмою різними способами: зміною змінних, зміною коду, запуском у режимах відстеження або налагодження ... будь-що.

Дозвольте мені пояснити, що життя вже не таке просте. Наприклад,

  • Багато перекладачів попередньо складатимуть код, який їм надають, тому крок перекладу не потрібно повторювати знову і знову.
  • Деякі компілятори компілюють не певні машинні інструкції, а байткод, якийсь штучний машинний код для вигаданої машини. Це робить компільовану програму трохи більш портативною, але вимагає інтерпретатора байт-коду для кожної цільової системи.
  • Інтерпретатори байт-кодів (я дивлюся на Java тут) останнім часом, як правило, перекомпілюють байт-код, який вони отримують для ЦП цільового розділу безпосередньо перед виконанням (називається JIT). Щоб заощадити час, це часто робиться лише для коду, який працює часто (гарячі точки).
  • Деякі системи, які виглядають як інтерпретатори (наприклад, Clojure), негайно збирають будь-який код, який вони отримують, але дозволяють інтерактивний доступ до середовища програми. Це в основному зручність перекладачів зі швидкістю двійкового складання.
  • Деякі компілятори насправді не компілюють, вони просто попередньо перетравлюють і стискають код. Я почув деякий час тому, як працює Perl. Тому іноді компілятор лише трохи виконує роботу, і більшість із них все ще є інтерпретацією.

Зрештою, в наші дні інтерпретація та компіляція є компромісом, коли час, витрачений (один раз), часто отримує винагороду за кращої продуктивності роботи, але інтерпретаційне середовище дає більше можливостей для взаємодії. Компіляція та інтерпретація - це головним чином питання того, як робота "розуміння" програми розділена між різними процесами, і лінія дещо розмита в ці дні, оскільки мови та продукти намагаються запропонувати найкраще з обох світів.


23

Від http://www.quora.com/What-is-the-difference-bet between- compiled- and- interpreted- programming- languages

Немає різниці, оскільки "складена мова програмування" та "інтерпретована мова програмування" не є змістовними поняттями. Будь-яка мова програмування, і я справді маю на увазі будь-яку, може бути інтерпретована або складена. Таким чином, інтерпретація та компіляція - це методи реалізації, а не атрибути мов.

Інтерпретація - це техніка, згідно з якою інша програма, перекладач, виконує операції від імені програми, що інтерпретується, з метою її запуску. Якщо ви можете уявити собі, як читати програму і робити те, що їй сказано, робити покроково, скажіть на папері з нуля, це саме те, що робить і перекладач. Поширеною причиною інтерпретації програми є те, що інтерпретатори писати відносно легко. Інша причина полягає в тому, що перекладач може контролювати, що програма намагається робити під час її роботи, виконувати політику, скажімо, задля безпеки.

Компіляція - це техніка, згідно з якою програма, написана однією мовою ("мова джерела"), переводиться в програму іншою мовою ("мова об'єкта"), що, сподіваємось, означає те саме, що і оригінальна програма. Виконуючи переклад, компілятор звичайно намагається також перетворити програму способами, які дозволять зробити об'єктну програму швидшою (не змінюючи її значення!). Поширеною причиною складання програми є те, що існує якийсь хороший спосіб запустити програми на мові об'єкта швидко та без накладних витрат на інтерпретацію вихідної мови.

Ви, мабуть, здогадалися, виходячи з вищенаведених визначень, що ці дві методи реалізації не є взаємовиключними та можуть навіть доповнювати один одного. Традиційно мовою об'єкта компілятора був машинний код або щось подібне, що стосується будь-якої кількості мов програмування, зрозумілих для конкретних комп'ютерних процесорів. Потім машинний код запускається «на металі» (хоча, якщо достатньо придивитись, можна побачити, що «метал» працює як перекладач). Однак сьогодні дуже часто використовувати компілятор для генерування об'єктного коду, який призначений для інтерпретації - наприклад, так працює (а іноді й досі) працює Java. Існують компілятори, які перекладають інші мови на JavaScript, який часто запускається у веб-браузері, який може інтерпретувати JavaScript, або скласти на ньому віртуальну машину або власний код. У нас також є інтерпретатори машинного коду, які можна використовувати для емуляції одного виду обладнання на інший. Або можна використовувати компілятор для створення об'єктного коду, який є вихідним кодом іншого компілятора, який може навіть компілювати код в пам'яті саме вчасно, щоб він запустився, що в свою чергу. . . ви отримуєте ідею. Існує багато способів поєднання цих понять.


Чи можете ви виправити це речення: "Є компілятори, які перекладають інші мови на JavaScript, який потім часто запускається у веб-браузері, який може інтерпретувати JavaScript або компілювати його у віртуальну машину або нативний код."
Корай Тугай

Прибив його. Ще одна поширена помилка - віднести корисність мови до існуючих API.
Маленький Ендіан

10

Найбільша перевага інтерпретованого вихідного коду перед складеним вихідним кодом - ПОРТИВНІСТЬ .

Якщо ваш вихідний код зібраний, вам потрібно скомпілювати інший виконуваний файл для кожного типу процесора та / або платформи, на якій ви хочете працювати з вашою програмою (наприклад, один для Windows x86, один для Windows x64, один для Linux x64 і так далі на). Крім того, якщо ваш код повністю не відповідає стандартам і не використовує будь-яких функцій / бібліотек для платформи, вам потрібно буде писати та підтримувати кілька баз коду!

Якщо ваш вихідний код інтерпретується, його потрібно написати лише один раз, і його можна інтерпретувати та виконувати відповідним перекладачем на будь-якій платформі! Це портативний ! Зверніть увагу , що сам інтерпретатор виконується програма , яка буде написана і складена для конкретної платформи.

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


1
на цьому терміні Java не можна вважати "компільованою мовою", але фаза компіляції надає переваги компіляції (перевірка типу, раннє виявлення помилок тощо) та створює байт-код, який можна запустити на будь-якій ОС, з Java віртуальна машина надана.
Rogelio Triviño

7

Компілятор та інтерпретатор виконують ту саму роботу: переводять мову програмування на іншу мову пграммінг, як правило, ближчу до апаратної, часто прямий виконуваний машинний код.

Традиційно "компільований" означає, що цей переклад відбувається за один раз, робиться розробником, а отриманий виконуваний файл поширюється серед користувачів. Чистий приклад: C ++. Компіляція зазвичай займає досить багато часу і намагається зробити багато дорогої оптимізації, щоб результат, який виконується, працював швидше. Кінцеві користувачі не мають інструментів та знань, щоб самостійно збирати речі, і виконуваний файл часто доводиться працювати на різноманітних апаратних засобах, тому ви не можете робити багато оптимізацій для конкретних апаратних засобів. Під час розробки окремий етап компіляції означає більш тривалий цикл зворотного зв'язку.

Традиційно "інтерпретований" означає, що переклад відбувається "на льоту", коли користувач хоче запустити програму. Чистий приклад: PHP ванілі. Наївний перекладач повинен розбирати та перекладати кожен фрагмент коду кожного разу, коли він працює, що робить його дуже повільним. Він не може робити складні, затратні оптимізації, оскільки це займе більше часу, ніж час, що зберігається на виконанні. Але він може повністю використовувати можливості обладнання, на якому він працює. Відсутність окремого кроку компіляції скорочує час зворотного зв’язку під час розробки.

Але в наш час "складено проти інтерпретованого" не є чорно-білим питанням, між ними є відтінки. Наївні, прості перекладачі в значній мірі вимерли. Багато мов використовують двоступеневий процес, коли код високого рівня переводиться на незалежний від платформи байт-код (який набагато швидше інтерпретувати). Тоді у вас є "просто вчасно компілятори", які компілюють код не більше одного разу запуск програми, іноді результати кешування, і навіть розумно вирішують інтерпретувати код, який запускається рідко, і роблять потужні оптимізації для коду, який працює багато. Під час розробки налагоджувачі здатні перемикати код всередині запущеної програми навіть для традиційно складених мов.


1
Однак модель компіляції C ++ успадкована від C і була розроблена без урахування таких функцій, як шаблони. Ця незграбність сприяє тривалому складанню C ++ набагато більше, ніж будь-який інший фактор - і робить його поганим прикладом.

4

По-перше, уточнення, Java не є повністю статично складеною і пов'язаною способом C ++. Він компілюється в байт-код, який потім інтерпретується JVM. JVM може переходити та робити своєчасну компіляцію до рідної мови машини, але цього не потрібно робити.

Більше до речі: я думаю, що інтерактивність - це головна практична відмінність. Оскільки все трактується, ви можете взяти невеликий уривок коду, проаналізувати та запустити його проти поточного стану навколишнього середовища. Таким чином, якби ви вже виконували код, який ініціалізував змінну, ви мали б доступ до цієї змінної і т. Д. Він дійсно піддається таким речам, як функціональний стиль.

Інтерпретація, однак, коштує багато, особливо коли у вас є велика система з великою кількістю посилань та контексту. За визначенням, це марно, тому що ідентичний код, можливо, доведеться інтерпретувати та оптимізувати двічі (хоча у більшості циклів виконання є деякі кешування та оптимізації для цього). Тим не менш, ви платите витрати на виконання і часто потребуєте середовища виконання. Ви також рідше бачите складні міжпроцедурні оптимізації, оскільки в даний час їх ефективність не є достатньо інтерактивною.

Тому для великих систем, які не збираються сильно змінюватись, а для певних мов є більш доцільним попередньо компілювати і попередньо зв’язувати все, робити всі оптимізації, які ви можете зробити. Це закінчується дуже худорлявим виконанням, яке вже оптимізоване для цільової машини.

Що стосується генерації executbles, це мало стосується цього, IMHO. Ви можете часто створювати виконуваний файл із мови компіляції. Але ви також можете створити виконуваний файл з інтерпретованої мови, за винятком того, що інтерпретатор і час виконання вже упаковані у виконанні та приховані від вас. Це означає, що ви, як правило, все-таки оплачуєте витрати на виконання (хоча я впевнений, що для деяких мов існують способи перевести все на дерево, яке виконується).

Я не погоджуюся, що всі мови можна зробити інтерактивними. Деякі мови, як-от C, настільки прив'язані до машини та всієї структури посилань, що я не впевнений, що ви можете створити повноцінну повноцінну інтерактивну версію


С насправді не прив’язаний до "машини". Синтаксис і семантика С досить прості. Впровадити C-інтерпретатор не має особливих труднощів, лише дуже трудомісткий (тому що стандартна бібліотека також повинна бути реалізована). І btw, Java може бути скомпільована в нативний машинний код (за допомогою gcj).
місячник

@lunaryorn: Я не згоден з приводу GCJ. GCJ просто дає вам середовище на основі виконання. "Скомпільовані додатки пов'язані з програмою GCJ, libgcj, яка забезпечує бібліотеки основного класу, збирач сміття та інтерпретатор байт-коду"
Uri

2
GCJ робить нативний машинний код, а не тільки виконується середу з вбудованим інтерпретатором і байткод. libgcj надає інтерпретатор байт-кодів для підтримки дзвінків з рідного коду в байт-код Java, а не для інтерпретації складеної програми. Якщо libgcj не надав інтерпретатора байт-коду, GCJ не відповідав би специфікації Java.
місячник

@lunaryorn: Ага. Гаразд, я вдячний за уточнення та виправлення. Ми в основному використовуємо Java в середовищі Windows, тому я не пробував gcj протягом багатьох років.
Урі


2

Практичну відповідь досить складно дати, оскільки різниця полягає у самому мовному визначенні. Можна створити інтерпретатор для кожної мови, що компілюється, але неможливо створити компілятор для кожної мови, що інтерпретується. Мова йде дуже про формальне визначення мови. Так що теоретичні інформатики нобію подобаються в університеті.


1
Звичайно, ви можете створити компілятор для інтерпретованої мови, але скомпільований машин-код сам по собі є дзеркалом часу виконання.
Ейден Белл

2

Книга Python © 2015 Imagine Publishing Ltd, просто зменшує різницю наступним підказом, зазначеним на сторінці 10, як:

Інтерпретована мова, така як Python, - це мова, де вихідний код перетворюється на машинний код та виконується щоразу при запуску програми. Це відрізняється від компільованої мови, такої як C, де вихідний код перетворюється на машинний код лише один раз - отриманий машинний код потім виконується щоразу при запуску програми.


1

Compile - це процес створення виконуваної програми з коду, написаного на складеній мові програмування. Компіляція дозволяє комп'ютеру запускати та розуміти програму без необхідності програмного забезпечення, яке використовується для її створення. Коли компілюється програма, вона часто компілюється для певної платформи (наприклад, платформи IBM), яка працює з сумісними комп'ютерами IBM, але не з іншими платформами (наприклад, платформи Apple). Перший компілятор був розроблений Грейс Хоппер під час роботи на комп'ютері Гарвард Марк I. Сьогодні більшість мов високого рівня включатимуть власний компілятор або наявні набори інструментів, які можна використовувати для компіляції програми. Хорошим прикладом компілятора, який використовується в Java, є Eclipse, а прикладом компілятора, який використовується з C і C ++, є команда gcc.


0

Коротке (неточне) визначення:

Мова компіляції: Вся програма переводиться одразу на машинний код, потім машинним кодом керує процесор.

Інтерпретована мова: Програма читається по черзі, і як тільки читається рядок, процесор виконує машинні інструкції для цього рядка.

Але насправді мало мов у ці дні є чисто складеними або чисто інтерпретованими, часто це суміш. Більш детальний опис із зображеннями дивіться у цій темі:

Яка різниця між складанням та інтерпретацією?

Або моя пізніша публікація в блозі:

https://orangejuiceliberationfront.com/the-difference-bet between-compiler-and-interpreter/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.