コンパイルされた言語と解釈された言語


284

私は違いをよりよく理解しようとしています。私はオンラインで多くの説明を見つけましたが、実際的な影響よりも抽象的な違いに向かう傾向があります。

私のプログラミング経験のほとんどは、CPython(動的、解釈済み)、およびJava(静的、コンパイル済み)での経験があります。ただし、他の種類のインタープリター型およびコンパイル型言語があることは理解しています。コンパイルされた言語で記述されたプログラムから実行可能ファイルを配布できるという事実は別として、各タイプに利点/欠点はありますか?インタプリタ言語はインタラクティブに使用できると人々が主張することをよく耳にしますが、コンパイルされた言語にもインタラクティブな実装があると思いますよね?


32
この比較では、最悪の言語を選択しました。どちらもバイトコンパイルされています。それらの間の唯一の本当の違いはJITerであり、Pythonにも部分的なものがあります(psyco)。
Ignacio Vazquez-Abrams

1
インタラクティブなコンパイル言語の良い例はClojureです。すべてが完全にコンパイルされています(最初にJVMに、次にJITを介してネイティブコードに)。ただし、再コンパイルの多くは動的に行われ、開発は対話型REPLシェルで行われることが多く、実行環境で必要な関数を評価できます。
mikera

標準MLは、もう1つの対話型コンパイル言語です。組み込みコンパイラは、実際のネイティブマシンコードも発行します。
ドナルフェロー


回答:


459

コンパイルされた言語は、プログラムがコンパイルされると、ターゲットマシンの命令で表現される言語です。たとえば、ソースコードの「+」演算は、機械語の「ADD」命令に直接変換できます。

インタプリタ言語とは、命令がターゲットマシンによって直接実行されるのではなく、他のプログラム(通常ネイティブマシンの言語で記述されている)によって読み取られて実行される言語です。たとえば、同じ「+」操作は実行時にインタープリターによって認識され、適切な引数を使用して独自の「add(a、b)」関数を呼び出し、マシンコードの「ADD」命令を実行します。 。

コンパイル済み言語のインタープリター言語でできることは何でもできますし、その逆も可能です。どちらもチューリング完全です。ただし、どちらにも実装と使用には利点と欠点があります。

私は完全に一般化するつもりです(純粋主義者は私を許します!)、大まかに、ここにコンパイルされた言語の利点があります:

  • ターゲットマシンのネイティブコードを直接使用することにより、より高速なパフォーマンス
  • コンパイル段階で非常に強力な最適化を適用する機会

そして、ここにインタープリター言語の利点があります:

  • 実装が簡単(優れたコンパイラを書くのは非常に難しい!!)
  • コンパイル段階を実行する必要はありません。「オンザフライ」で直接コードを実行できます
  • 動的言語に便利です

バイトコードコンパイルなどの最新のテクニックは、さらに複雑さを追加します。ここで何が起こるかは、コンパイラが、基礎となるハードウェアと同じではない「仮想マシン」をターゲットとすることです。その後、これらの仮想マシン命令を後の段階で再度コンパイルして、ネイティブコードを取得できます(Java JVM JITコンパイラーによって行われるように)。


1
すべてのコンパイル済み言語が遅いコンパイル段階を必要とするわけではありません。深刻なCommon Lispの実装はコンパイラーであり、インタープリターに煩わされることはほとんどなく、その場で実際に高速でコンパイルすることを好みます。一方、Javaにはコンパイル手順が必要であり、通常は表示されます。
David Thornley、2010

2
@Kareem:JITコンパイラーは1)と2)を1回だけ実行します。その後はネイティブコードになります。インタプリタは、コードが呼び出されるたびに1)と2)の両方を実行する必要があります(何回も、何回も...)。したがって、時間の経過とともに、JITコンパイラーは長いマージンで勝っています。
mikera 2013

3
はい、バイトコードはプログラム全体の実行中のある時点でマシンコードに変換されます(従来のコンパイラーの場合のようにプログラム実行前とは異なります)。しかし、特定のコードは、プログラム全体の実行中に1000万回以上実行される可能性があります。それは(おそらく)バイトコードからマシンコードへ一度だけコンパイルされます。したがって、JITの実行時のオーバーヘッドは小さく、実行時間の長いプログラムでは無視できます。JITコンパイラーの処理が完了すると、純粋なマシンコードを完全に実行できます。
mikera 2013

2
これは実際には誤った二分法です。私たちの解釈をコンパイルさせる言語に固有のものは何もありません。それは、広く受け入れられている誤解に過ぎません。多くの言語には両方の実装があり、すべての言語はどちらかを実装できます。
mmachenry 2014年

2
@mmachenryそれは誤った二分法ではありません。「プログラミング言語」には、設計と実装の両方が含まれます。しながら、理論的与えられた言語の定義は、両方のコンパイルと解釈することができ、中に現実の世界の練習実装にかなりの違いがあります。たとえば、特定の言語構成要素を効果的にコンパイルする方法はまだ誰も解決していません。これはオープンリサーチの問題です。
mikera 2014年

99

言語自体はコンパイルも解釈もされず、言語の特定の実装のみがコンパイルされます。Javaは完璧な例です。バイトコードベースのプラットフォーム(JVM)、ネイティブコンパイラー(gcj)、およびJavaのスーパーセット(bsh)のインターピーターがあります。では、Javaとは何でしょうか。バイトコードコンパイル、ネイティブコンパイル、または解釈?

コンパイルおよび解釈される他の言語は、Scala、Haskell、またはOcamlです。これらの各言語には、対話型インタープリターと、バイトコードまたはネイティブマシンコードへのコンパイラーがあります。

したがって、一般的に「コンパイル済み」と「解釈済み」で言語を分類することは、あまり意味がありません。


3
同意する。あるいは、ネイティブコンパイラ(CPUが食べるためのマシンコードを作成する)とそうでないコンパイラ(トークン化されたもの、つまり中間コードを作成する)があり、一部のジャストインタイムコンパイラは以前にマシンコードにコンパイルする(または実行時ONCE)、およびマシンコードを生成せず、CPUにコードを実行させない「実際の」非コンパイラーがあります。後者は通訳です。今日、コンパイル時にマシン(CPU)コードを直接生成するネイティブコンパイラは、ますます珍しくなっています。Delphi / Codegearは最高の生存者の1人です。
TheBlastOne 2010

57

Aの観点から考え始める:過去からの爆発

むかしむかし昔、コンピューティングインタープリタとコンパイラの領域に住んでいました。あらゆる種類の大騒ぎが、一方の他方のメリットよりも続いた。当時の一般的な見解は、次のようなものでした。

  • インタープリター:開発(編集および実行)が高速です。各ステートメントが実行されるたびにマシンコードに解釈される必要があるため、実行が遅くなります(これが何千回もループが実行されることの意味を考えてください)。
  • コンパイラー:開発に時間がかかる(編集、コンパイル、リンク、実行。コンパイル/リンクのステップには、かなりの時間がかかる可能性があります)。実行が速い。プログラム全体はすでにネイティブマシンコードに含まれていました。

インタプリタされたプログラムとコンパイルされたプログラムの間には、ランタイムパフォーマンスに1桁または2桁の違いがありました。たとえばコードの実行時の可変性など、他の際立った点にも関心がありましたが、主な違いは実行時のパフォーマンスの問題に関係しています。

今日、状況は進化しており、コンパイル/解釈された区別はほとんど無関係です。多くのコンパイルされた言語は、完全にマシンコードベースではないランタイムサービスを必要とします。また、ほとんどのインタプリタ言語は、実行前にバイトコードに「コンパイル」されます。バイトコードインタープリターは非常に効率的で、実行速度の観点からコンパイラー生成コードに匹敵します。

古典的な違いは、コンパイラーがネイティブマシンコードを生成し、インタープリターがソースコードを生成し、ある種のランタイムシステムを使用してオンザフライでマシンコードを生成したことです。今日、クラシックインタープリターはほとんど残っていません。ほとんどすべてがバイトコード(または他のセミコンパイルされた状態)にコンパイルされ、仮想「マシン」で実行されます。


1
いいですね-前の段落のすばらしい要約-ありがとうございます!
ckib16 2018

26

極端で単純なケース:

  • コンパイラは、ターゲットマシンのネイティブ実行可能形式でバイナリ実行可能ファイルを生成します。このバイナリファイルには、システムライブラリを除くすべての必要なリソースが含まれています。コードはターゲットマシンのCPUのネイティブコードであるため、それ以上の準備や処理を行わずに実行する準備が整い、稲妻のように実行されます。

  • インタープリターは、ステートメントまたはコードを入力できるループでプロンプトをユーザーに表示し、ヒットRUNまたは同等の機能でインタープリターは、プログラムが停止点またはエラーまで実行されるまで、各行を検査、スキャン、解析、および解釈的に実行します。 。各行はそれ自体で処理され、インタープリターは以前にその行を見たことから何も「学習」しないので、人間が読める言語を機械命令に変換する作業は、すべての行で毎回発生するため、時間がかかります。明るい面では、ユーザーは、変数の変更、コードの変更、トレースモードまたはデバッグモードでの実行など、あらゆる方法でプログラムを検査したり、その他の方法でプログラムと対話したりできます。

それらが邪魔になると、私は人生がもはやそれほど単純ではないことを説明させてください。例えば、

  • 多くのインタープリターは与えられたコードをプリコンパイルするので、翻訳ステップを何度も繰り返す必要はありません。
  • 一部のコンパイラは、CPU固有のマシン命令ではなく、架空のマシン用の一種の人工マシンコードであるバイトコードにコンパイルされます。これにより、コンパイルされたプログラムは少しポータブルになりますが、すべてのターゲットシステムでバイトコードインタープリターが必要になります。
  • バイトコードインタープリター(ここではJavaを調べています)は最近、実行直前のターゲットセクションのCPU(JITと呼ばれます)用に取得したバイトコードを再コンパイルする傾向があります。時間を節約するために、これは多くの場合、頻繁に実行されるコード(ホットスポット)に対してのみ行われます。
  • インタプリタのように見えて動作する一部のシステム(Clojureなど)は、取得したコードをすぐにコンパイルしますが、プログラムの環境へのインタラクティブなアクセスを許可します。これは基本的に、バイナリコンパイルの速度が速いインタプリタの利便性です。
  • 一部のコンパイラは実際にはコンパイルせず、コードを事前にダイジェストして圧縮します。しばらく前に、それがPerlの動作方法だと聞きました。そのため、コンパイラーが少しの作業を行っているだけで、そのほとんどはまだ解釈です。

結局のところ、最近では、解釈とコンパイルはトレードオフの関係にあり、コンパイルに費やした時間(1回)は多くの場合、実行時のパフォーマンスが向上しますが、解釈環境では対話の機会が増えます。コンパイルと解釈は、ほとんどの場合、プログラムを「理解」する作業が異なるプロセス間でどのように分割されるかに関する問題であり、言語と製品が両方の世界のベストを提供しようとする今日、線は少しぼやけています。


23

http://www.quora.com/What-is-the-difference-between-compiled-and-interpreted-programming-languagesから

「コンパイルされたプログラミング言語」と「解釈されたプログラミング言語」は意味のある概念ではないため、違いはありません。どんなプログラミング言語でも、そして私が本当にどんなものでも、解釈またはコンパイルすることができます。したがって、解釈とコンパイルは実装手法であり、言語の属性ではありません。

解釈とは、別のプログラムであるインタプリタが、プログラムを実行するために解釈されるプログラムに代わって操作を実行する手法です。あなたがプログラムを読んで、それがステップバイステップで言うことを、例えば一枚の紙の上で言うことを想像できるならば、それはまさに通訳が行うことでもあります。プログラムを解釈する一般的な理由は、インタプリタが比較的簡単に作成できることです。もう1つの理由は、インタプリタがプログラムの実行時に実行しようとすることを監視して、セキュリティなどのポリシーを適用できるためです。

コンパイルとは、ある言語(「ソース言語」)で書かれたプログラムを別の言語(「オブジェクト言語」)のプログラムに翻訳する手法です。これは、元のプログラムと同じことを意味します。翻訳をしている間、コンパイラがオブジェクトプログラムをより速くする方法でプログラムを変換しようとすることは一般的です(その意味を変えることなく!)。プログラムをコンパイルする一般的な理由は、オブジェクト言語でプログラムをすばやく実行するための適切な方法があり、途中でソース言語を解釈するオーバーヘッドがないことです。

上記の定義に基づいて、これらの2つの実装手法は相互に排他的ではなく、補完的でさえあると推測したかもしれません。伝統的に、コンパイラーのオブジェクト言語は、マシンコードまたはそれに類似したものでした。これは、特定のコンピューターCPUによって理解される任意の数のプログラミング言語を指します。マシンコードは「金属上」で実行されます(ただし、よく見ると、「金属」がインタプリタのように機能していることがわかります)。しかし今日では、コンパイラーを使用して解釈されるオブジェクトコードを生成することは非常に一般的です。たとえば、これはJavaがかつて(そして時にはまだ)機能していた方法です。他の言語をJavaScriptに変換するコンパイラーがあり、それはしばしばJavaScriptを解釈する可能性があるWebブラウザーで実行されます。または、仮想マシンまたはネイティブコードをコンパイルします。マシンコードのインタープリターもあり、これを使用して、ある種類のハードウェアを別の種類のハードウェアでエミュレートできます。または、コンパイラを使用して別のコンパイラのソースコードであるオブジェクトコードを生成することもできます。これにより、メモリ内のコードがコンパイルされて実行され、実行されます。。。あなたはアイデアを得ます。これらの概念を組み合わせるには多くの方法があります。


この文を修正できますか:「他の言語をJavaScriptに翻訳するコンパイラーがあり、それはしばしばWebブラウザーで実行され、JavaScriptを解釈するか、仮想マシンまたはネイティブコードをコンパイルする可能性があります。」
Koray Tugay

ばっちり成功。もう1つのよくある間違いは、言語の有用性を既存のAPIに帰することです。
リトルエンディアン

10

コンパイルされたソースコードに対するインタープリター型ソースコードの最大の利点は、移植性です。

ソースコードがコンパイルされている場合は、プログラムを実行するプロセッサやプラットフォームのタイプごとに異なる実行可能ファイルをコンパイルする必要があります(Windows x86用、Windows x64用、Linux x64用など)。オン)。さらに、コードが完全に標準に準拠し、プラットフォーム固有の関数/ライブラリを使用しない場合を除き、実際には複数のコードベースを作成して維持する必要があります。

ソースコードが解釈される場合、一度書くだけで、どのプラットフォームの適切なインタープリターでも解釈して実行できます!それはですポータブル!インタプリタ自体は実行可能プログラムであることに注意された特定のプラットフォーム用に書かれ、コンパイルされました。

コンパイルされたコードの利点は、元の人間が読めるソースコードを展開する代わりに、あいまいなバイナリ実行可能ファイルを展開するため、ソースコードがエンドユーザー(知的財産である可能性があります)から隠されることです。


1
この用語では、javaは「コンパイルされた言語」と見なすことはできませんが、コンパイルフェーズはコンパイルの利点(型チェック、早期エラー検出など)を提供し、Javaを使用してすべてのOSで実行できるバイトコードを生成します提供される仮想マシン。
RogelioTriviño2017年

7

コンパイラーとインタープリターは同じ働きをします。プログラミング言語を別のpgoramming言語に変換します。通常はハードウェアに近く、多くの場合、直接実行可能なマシンコードです。

従来、「コンパイル済み」とは、この変換がすべて一度に行われ、開発者によって行われ、結果の実行可能ファイルがユーザーに配布されることを意味します。純粋な例:C ++。コンパイルは通常かなり時間がかかり、結果の実行可能ファイルがより速く実行されるように多くの高価な最適化を試みます。エンドユーザーは、自分でコンテンツをコンパイルするためのツールや知識を持っていません。実行可能ファイルはさまざまなハードウェアで実行する必要があることが多いため、ハードウェア固有の最適化を多く行うことはできません。開発中、個別のコンパイルステップはフィードバックサイクルが長くなることを意味します。

従来、「解釈済み」とは、ユーザーがプログラムを実行したいときに、「オンザフライ」で翻訳が行われることを意味します。純粋な例:バニラPHP。単純なインタプリタは、実行するたびにすべてのコードを解析および変換する必要があるため、非常に遅くなります。実行にかかる時間よりも時間がかかるため、複雑で費用のかかる最適化を行うことはできません。ただし、実行するハードウェアの機能を完全に使用できます。個別のコンパイル手順がないため、開発中のフィードバック時間が短縮されます。

しかし、現在、「コンパイルされたものと解釈されたもの」は白黒の問題ではなく、中間に色合いがあります。素朴で単純な通訳はほとんど絶滅しています。多くの言語は、高レベルのコードがプラットフォームに依存しないバイトコードに変換される2段階のプロセスを使用します(解釈がはるかに高速です)。次に、プログラムの実行ごとに最大1回コードをコンパイルする「ジャストインタイムコンパイラ」があり、結果をキャッシュし、まれにしか実行されないコードを解釈し、頻繁に実行されるコードに対して強力な最適化を行うことさえできます。開発中、デバッガーは、従来のコンパイル言語でも、実行中のプログラム内のコードを切り替えることができます。


1
ただし、C ++のコンパイルモデルはCから継承されており、テンプレートなどの機能を考慮せずに設計されています。このぎこちなさは、C ++の長いコンパイル時間を他のどの要因よりもはるかに大きくし、それを悪い例にしています。

4

まず、説明として、JavaはC ++の方法で完全に静的にコンパイルおよびリンクされていません。これはバイトコードにコンパイルされ、JVMによって解釈されます。JVMは、ネイティブマシン言語へのジャストインタイムのコンパイルを実行できますが、実行する必要はありません。

要点は次のとおりです。インタラクティブ機能が主な実用的な違いだと思います。すべてが解釈されるので、コードの小さな抜粋を取得して、環境の現在の状態に対して解析および実行できます。したがって、変数を初期化するコードをすでに実行している場合は、その変数などにアクセスできます。これは、関数スタイルなどに役立ちます。

ただし、解釈には多くのコストがかかります。特に、参照とコンテキストが多い大規模なシステムの場合はそうです。定義上、同一のコードを2回解釈して最適化する必要がある場合があるため、これは無駄です(ほとんどのランタイムには、キャッシュと最適化がいくつかあります)。それでも、ランタイムコストを支払い、ランタイム環境が必要になることがよくあります。また、現時点ではパフォーマンスが十分にインタラクティブではないため、複雑なプロシージャー間の最適化が見られる可能性も低くなります。

したがって、大きな変更が加えられない大規模なシステム、および特定の言語では、すべてをプリコンパイルおよびプリリンクして、実行できるすべての最適化を行う方が理にかなっています。これにより、ターゲットマシン用に既に最適化された非常にリーンなランタイムが実現します。

実行ファイルを生成することに関しては、それは私見とはほとんど関係ありません。多くの場合、コンパイルされた言語から実行可能ファイルを作成できます。ただし、インタープリターとランタイムがすでに実行可能ファイルにパッケージ化され、ユーザーから隠されている場合を除いて、インタープリター言語から実行可能ファイルを作成することもできます。これは、一般的には依然としてランタイムコストを支払うことを意味します(一部の言語では、すべてをツリーの実行可能ファイルに変換する方法があると確信しています)。

すべての言語をインタラクティブにすることができるとは思わない。Cのような特定の言語はマシンとリンク構造全体に非常に関連しているため、意味のある本格的なインタラクティブバージョンを構築できるかどうかはわかりません。


Cは実際には「マシン」に関連付けられていません。Cの構文とセマンティクスはかなり単純です。Cインタープリターを実装することはそれほど難しくはなく、非常に時間がかかります(標準ライブラリも実装する必要があるため)。ところで、Javaはネイティブマシンコードにコンパイルできます(gcjを使用)。
lunaryorn 2010

@lunaryorn:GCJに同意しません。GCJは単に実行可能ベースの環境を提供するだけです。「コンパイルされたアプリケーションは、コアクラスライブラリ、ガベージコレクター、およびバイトコードインタープリターを提供するGCJランタイムlibgcjとリンクしています」
Uri

2
GCJ 、インタープリターとバイトコードが埋め込まれた実行可能環境だけでなく、ネイティブマシンコードを生成します。libgcjは、コンパイルされたプログラムを解釈するのではなく、ネイティブコードからJavaバイトコードへの呼び出しをサポートするバイトコードインタープリターを提供します。libgcjがバイトコードインタープリターを提供しなかった場合、GCJはJava仕様に準拠しません。
lunaryorn

@lunaryorn:ああ。わかりました、私は説明を感謝し、修正しました。私たちは主にWindows環境でJavaを使用しているため、私は何年もgcjを試していません。
ウリ


2

違いは言語の定義自体にあるため、実際的な答えを出すのはかなり困難です。すべてのコンパイル済み言語のインタープリターを構築することは可能ですが、すべてのインタープリター言語用のコンパイラーを構築することはできません。それは、言語の正式な定義に関するものです。だから、理論的な情報学のものは誰もが大学で好きではありません。


1
確かにインタプリタ言語用のコンパイラを構築できますが、コンパイルされたマシンコード自体はランタイムのミラーです。
エイデンベル

2

Python Book©2015 Imagine Publishing Ltdは、10ページで言及されている次のヒントによって、その違いを明らかにしています。

Pythonなどのインタープリター型言語は、ソースコードがマシンコードに変換され、プログラムが実行されるたびに実行される言語です。これは、ソースコードが一度だけマシンコードに変換されるCなどのコンパイル済み言語とは異なります。結果のマシンコードは、プログラムが実行されるたびに実行されます。


1

コンパイルは、コンパイルされたプログラミング言語で書かれたコードから実行可能プログラムを作成するプロセスです。コンパイルすることで、コンピューターはプログラムを作成するために使用するプログラミングソフトウェアを必要とせずに、プログラムを実行および理解できます。プログラムをコンパイルすると、多くの場合、IBM互換コンピューターで動作する特定のプラットフォーム(IBMプラットフォームなど)用にコンパイルされますが、他のプラットフォーム(Appleプラットフォームなど)では動作しません。最初のコンパイラは、ハーバードマークIコンピュータで作業中にグレースホッパーによって開発されました。今日、ほとんどの高水準言語には、独自のコンパイラーが含まれているか、プログラムのコンパイルに使用できるツールキットが用意されています。Javaで使用されるコンパイラーの良い例はEclipseであり、CおよびC ++で使用されるコンパイラーの例はgccコマンドです。


0

短い(不正確な)定義:

コンパイルされた言語:プログラム全体が一度に機械コードに変換され、機械語コードはCPUによって実行されます。

解釈された言語:プログラムは1行ずつ読み込まれ、1行が読み込まれるとすぐに、その行の機械命令がCPUによって実行されます。

しかし、実際には、最近、純粋にコンパイルまたは完全に解釈されている言語はほとんどなく、多くの場合、それらは混在しています。写真付きの詳細な説明については、次のスレッドを参照してください。

コンパイルと解釈の違いは何ですか?

または私の後のブログ投稿:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.