Interpretowane a skompilowane: przydatne rozróżnienie?

29

Zadaje się tutaj wiele pytań na temat interpretowanych i skompilowanych narzędzi językowych. Zastanawiam się, czy to rozróżnienie rzeczywiście ma jakiś sens. (W rzeczywistości pytania zwykle dotyczą języków, ale naprawdę myślą o najpopularniejszych implementacjach tych języków).

Obecnie prawie żadna implementacja nie jest ściśle interpretowana. tzn. prawie nikt nie analizuje i nie uruchamia kodu po jednej linii na raz. Ponadto implementacja, która kompiluje się do kodu maszynowego, również staje się coraz mniej powszechna. Coraz częściej kompilatory są ukierunkowane na jakąś maszynę wirtualną.

W rzeczywistości większość wdrożeń jest zbieżna w oparciu o tę samą podstawową strategię. Kompilator wytwarza kod bajtowy, który jest interpretowany lub kompilowany do kodu natywnego za pomocą JIT. To naprawdę połączenie tradycyjnych pomysłów kompilacji i interpretacji.

Dlatego pytam: czy istnieje obecnie użyteczne rozróżnienie między interpretowanymi implementacjami a skompilowanymi implementacjami?

Winston Ewert
źródło
7
@DeadMG Nie tak nowy, jak może się wydawać: krótka historia just-in-time ...
yannis
4
@DeadMG Biorąc pod uwagę, że większość nowych języków wprowadzonych w ciągu ostatnich 10 lat działa głównie na pewnego rodzaju maszynach wirtualnych, powiedziałbym, że ma rację. Oczywiście nadal istnieją (i będą przez dziesięciolecia) języki skompilowane do natywnego kodu, a JIT pozostanie luksusowy (lub nie, jeśli ludzie PyPy mają na to sposób). Tak, możliwe zawyżenie, ale zgadzam się, że główny nurt (na razie i dająca się przewidzieć przyszłość) wydaje się być kompilatorem kodu bajtowego + prawdopodobnie JIT.
4
@DeadMG, musisz mieć długą białą brodę, jeśli model maszyny wirtualnej jest dla Ciebie „nowy”. P-codezostały wprowadzone w 1966 roku. IBM Aix istnieje od 1986 roku.
SK-logic
6
Rzeczy takie jak powłoki unixowe, Tcl i podobne zawsze byłyby interpretowane czysto, więc rozróżnienie ma sens, przynajmniej w akademickim CS. Ale prawdą jest, że gdy koderzy mamroczą o interpretatorach vs. kompilatorach, w większości przypadków nie mają sensu.
SK-logic
3
@ SK-logic, myślę, że twój komentarz jest lepszą odpowiedzią niż jakakolwiek z faktycznie opublikowanych odpowiedzi
Winston Ewert

Odpowiedzi:

23

Ważne jest, aby pamiętać, że tłumaczenie i kompilacja to nie tylko alternatywy dla siebie. Ostatecznie każdy napisany program (w tym skompilowany do kodu maszynowego) zostanie zinterpretowany. Interpretacja kodu oznacza po prostu pobranie zestawu instrukcji i zwrócenie odpowiedzi.

Z drugiej strony kompilacja oznacza konwersję programu z jednego języka na inny. Zwykle przyjmuje się, że podczas kompilacji kod jest kompilowany do języka „niższego poziomu” (np. Kod maszynowy, kod bajtowy VM itp.). Ten skompilowany kod jest nadal interpretowany później.

Jeśli chodzi o pytanie, czy istnieje użyteczna różnica między językami tłumaczonymi a skompilowanymi, osobiście uważam, że każdy powinien mieć podstawową wiedzę na temat tego, co dzieje się z kodem, który piszą podczas tłumaczenia. Tak więc, jeśli ich kod jest kompilowany JIT, buforowany kodem bajtowym itp., Programista powinien przynajmniej mieć podstawową wiedzę na temat tego, co to oznacza.

Zach Smith
źródło
3
Tak, programista powinien mieć podstawową wiedzę. Zastanawiam się jednak, czy skompilowana / zinterpretowana terminologia nie przeszkadza.
Winston Ewert
2
Dziękuję Ci!! Interpretowany jest tylko synonimem „wykonywanego” i tak działają wszystkie programy.
ogrodnik
9

Rozróżnienie to ma głębokie znaczenie, ponieważ języki skompilowane ograniczają semantykę w taki sposób, że języki interpretowane niekoniecznie. Niektóre techniki interpretacyjne są bardzo trudne (praktycznie niemożliwe) do skompilowania.

Zinterpretowany kod może wykonywać takie czynności, jak generowanie kodu w czasie wykonywania i udostępnianie tego kodu w powiązaniach leksykalnych istniejącego zakresu. To jeden przykład. Innym jest to, że tłumacze mogą zostać rozszerzeni o interpretowany kod, który może kontrolować sposób oceny kodu. To jest podstawa starożytnych „fexprs” Lispa: funkcje, które są wywoływane z nieocenionych argumentów i decydują, co z nimi zrobić (mając pełny dostęp do środowiska niezbędnego do przejścia kodu i oceny zmiennych itp.). W skompilowanych językach tak naprawdę nie można użyć tej techniki; zamiast tego używasz makr: funkcji, które są wywoływane w czasie kompilacji z nieocenionymi argumentami i tłumaczą kod zamiast interpretować.

Niektóre implementacje językowe są oparte na tych technikach; ich autorzy odrzucają kompilację jako ważny cel i raczej przyjmują tego rodzaju elastyczność.

Tłumaczenie zawsze będzie przydatne jako technika ładowania kompilatora. Konkretny przykład, spójrz na CLISP (popularna implementacja Common Lisp). CLISP ma kompilator, który jest napisany sam w sobie. Podczas kompilacji CLISP ten kompilator jest interpretowany na wczesnych etapach budowy. Służy do samodzielnej kompilacji, a następnie po skompilowaniu kompilacja jest wykonywana przy użyciu skompilowanego kompilatora.

Bez jądra interpretera będziesz musiał uruchomić się z istniejącym Lispem, podobnie jak SBCL.

Dzięki interpretacji możesz rozwijać język od zera, zaczynając od języka asemblera. Opracuj podstawowe procedury we / wy i podstawowe, a następnie napisz ewaluacyjny, wciąż maszynowy język. Po zakończeniu ewaluacji pisz w języku wysokiego poziomu; jądro kodu maszynowego dokonuje oceny. Użyj tego narzędzia, aby rozszerzyć bibliotekę o wiele innych procedur i napisać kompilator. Użyj kompilatora do skompilowania tych procedur i samego kompilatora.

Interpretacja: ważny krok na drodze do kompilacji!

Kaz
źródło
1
IMO, to najlepsza odpowiedź. Pracuję nad własnym językiem zabawek, a ostatni akapit opisuje sposób, w jaki go rozwijam. Naprawdę znacznie ułatwia pracę nad nowymi pomysłami. Również +1 za wzmiankę o procesie ładowania CLISP.
sinan
Teoretycznie każdy „interpretowany” język można przekształcić w „skompilowany”, generując plik EXE składający się z interpretera oraz kodu źródłowego lub kodu bajtowego interpretowanego programu. Jednak może nie być bardzo wydajny.
dan04
Przeczytaj, jak Wirth i in. Wymyślili kod P, aby uprościć przenoszenie PASCAL na nowe architektury maszyn. To było na początku lat siedemdziesiątych.
John R. Strohm,
1
Podejrzewam, że twój akapit początkowy myli kompilację i interpretację ze statycznym i dynamicznym zachowaniem, ale dam ci korzyść z wątpliwości i po prostu poproszę o przykład języka z semantyką, którego „praktycznie niemożliwa” jest kompilacja. Jeśli chodzi o ładowanie kompilatora, prawdą jest, że pierwsza implementacja musi być napisana w innym języku niż implementowany język, ale nie musi to być interpreter, może to być kompilator napisany w innym języku.
8bittree,
1

W rzeczywistości wiele implementacji języków jest wciąż ściśle interpretowanych, po prostu możesz ich nie znać. Aby wymienić kilka: języki powłoki UNIX, powłoki cmd Windows i PowerScript, Perl, awk, sed, MATLAB, Mathematica i tak dalej.

Charles E. Grant
źródło
3
Wierzę, że Perl jest wewnętrznie skompilowany do kodu bajtowego i przynajmniej Mathematica może zostać skompilowana. I nic nie dyktuje implementacji awk i sed (uważam, że niektóre jądra GNU kompilują wyrażenia regularne do automatów skończonych przed wykonaniem, co prawdopodobnie umieściłoby je w kategorii „kompilacja do reprezentacji pośredniej, interpretacja tej”).
1
Właściwie jestem całkiem pewien, że Perl, MATLAB, Mathematica kompilują się do kodu bajtowego. Nie znam PowerScript, masz na myśli Powershell? Jeśli tak, to przy użyciu CLR i tak samo używa kodu bajtowego.
Winston Ewert
@WinstonEwert, przepraszam, miałem na myśli PowerShell. Rozumiem, że tłumaczenie na formę pośrednią nie oznacza, że ​​coś nie jest interpretowane. Heck, oryginalny tłumacz BASIC z Dartmouth, tłumaczył źródło na tokeny przed tłumaczeniem. Każde z wymienionych przeze mnie narzędzi ma tryb, w którym 1) odczytuje wiersz źródłowy, 2) tłumaczy ten wiersz na postać wykonywalną (być może jakiś kod pośredni zamiast kodu rodzimego), 3) wykonuje kod dla tego wiersza, 4) powróć do 1). To odpowiada mojemu zrozumieniu tłumacza.
Charles E. Grant
2
Kod bajtowy oznacza kompilację. Kompilator kodu bajtowego to po prostu program, który pobiera źródło i konwertuje je na kod bajtowy. Dlatego wszystkie zastosowania kodu bajtowego muszą obejmować kompilator kodu bajtowego. Ale kod bajtowy również musi być interpretowany (lub JIT). Tak więc cokolwiek używającego kodu bajtowego jest hybrydą interpretera / kompilatora.
Winston Ewert
4
Naprawdę chodzi mi o to, że ludzie wyrzucają takie stwierdzenia, jak „Python jest interpretowany” i „Java jest kompilowany” bez zrozumienia implementacji. Zastanawiam się, czy opisywanie implementacji w tych kategoriach jest przydatne. Prawda jest zwykle bardziej skomplikowana, a próba sprowadzenia jej do interpretacji / kompilacji nie jest użyteczna.
Winston Ewert
1

Myślę: absolutnie tak .

W rzeczywistości większość wdrożeń jest zbieżna w oparciu o tę samą podstawową strategię

Naprawdę, C ++ ma na celu przeniesienie do domeny kompilatora jakiejś koncepcji wysokiego poziomu, która zwykle jest przekazywana tłumaczom, ale pozostaje po stronie mniejszości ...

CapelliC
źródło
2
Poczekaj, aż Clang + LLVM stanie się najpopularniejszym zestawem narzędzi kompilatora.
SK-logic
@ SK-logic, pomimo nazwy, uważam, że Clang + LLVM produkuje kod macierzysty.
Winston Ewert
1
@Winston Ewert, tylko jeśli chcesz. Możesz zatrzymać się na poziomie LLVM IR i zrobić z nim, co chcesz - zinterpretować, skompilować JIT, instrumentować w dowolny sposób. Możesz nawet przetłumaczyć go na Javascript, a następnie przejść przez tłumacza: github.com/kripken/emscripten/wiki
SK-logic
@ SK-logic, schludne rzeczy! Nie wiedziałem, że LLVM może to zrobić.
Winston Ewert
1
Piękno lvvm polega na celowym oddzieleniu przedniego i tylnego końca. Oraz narzędzia do manipulowania środkiem przed wybraniem zestawu instrukcji. Możesz scalić cały projekt w kod bajtowy, a następnie zoptymalizować całość, przy pomocy innych kompilatorów potrzebujesz jednego źródła plików lub przynajmniej jednego, który obejmuje drogę przez drzewo źródłowe, aby kompilator działał na jednym połączonym źródle. Również jeden zestaw narzędzi pod llvm jest ogólny dla wszystkich celów, nie musisz budować dla każdego celu, jeden kompilator pasuje do wszystkich (przynajmniej do asm celu).
old_timer
-1

Przydatne rozróżnienie: interpretowane programy mogą się modyfikować, dodając lub zmieniając funkcje w czasie wykonywania.

Diego Pacheco
źródło
8
Nonsens. Kod samomodyfikujący (maszynowy) to najstarsza sztuczka w książce. Z drugiej strony niektórzy twierdzą, że nawet natywny kod jest ostatecznie interpretowany przez interpreter rzutowany na krzem (CPU). Ale jeśli założymy, że cały kod jest interpretowany i nie ma żadnego rozróżnienia.
2
@delnan ma rację. Dodam tylko, że współczesne języki mogą się modyfikować, dynamicznie tworząc nowe klasy i ładując / rozładowując biblioteki (lub np. „Zespoły” w .NET)
Jalayn
5
Common Lisp jest kompilowany, ale nadal możesz łatwo zastępować definicje funkcji w środowisku wykonawczym.
SK-logic
To naprawdę interesująca i niezbędna (na przykład w Prologu) funkcja interpretacji.
CapelliC,