動的プログラミング言語は、様々な操作を実行時に決定し実行できるプログラミング言語の一種です。これはコンパイルフェーズとは異なります。変数、メソッド呼び出し、データ型に関する重要な決定は、プログラムの実行時に行われます。一方、静的言語では構造と型はコンパイル時に固定されます。動的言語は柔軟性を提供します。これにより、開発者はより適応性が高く簡潔なコードを書くことができます。
例えば、動的言語では、変数は整数で始まり、その後、明示的な型宣言なしに文字列を格納するように再代入できます。この動的型付けの機能により、より柔軟で制約の少ないコーディングが可能になります。開発者は、言語の制約ではなく、ロジックと機能に集中できます。
一部の動的言語はeval関数を提供しています。この関数は、言語のコードを含む文字列または抽象構文木を受け取り、それを実行します。このコードが式を表す場合、結果の値が返されます。Erik MeijerとPeter Draytonは、evalによる実行時コード生成と共有ライブラリによる動的ロードを区別し、多くの場合evalは高階関数(関数を文字列として渡す)やデシリアライズの実装にのみ使用されると警告しています。[1]
動的言語では、型またはオブジェクトシステムは通常、実行時に変更できます。これは、実行時定義から、または既存の型やオブジェクトのミックスインに基づいて新しいオブジェクトを生成することを意味します。また、継承や型ツリーを変更し、既存の型の振る舞い(特にメソッドの呼び出しに関して)を変更することも意味します。
多くの動的言語は動的型システムを備えているため、内部解釈のための値に基づく実行時型推論は一般的なタスクです。値型は解釈中に変化する可能性があるため、アトミック操作を実行する際に頻繁に使用されます。
静的プログラミング言語では(間接的にも)開発者がコンパイル前に使用するメモリのサイズを定義する必要があります(ポインタロジックで回避する場合を除く)。オブジェクトの実行時変更と整合して、動的言語ではプログラム個々の操作に基づいて暗黙的にメモリを(再)割り当てする必要があります。
リフレクションは多くの動的言語で一般的であり、通常はジェネリックデータやポリモーフィックデータの型とメタデータの解析を伴います。 しかし、LispがS式解析に提供する機能のように、プログラムコードをデータとして完全に評価し、変更することも含まれる場合があります。
限られた数の動的プログラミング言語が、マクロと呼ばれる機能で、コード イントロスペクション(クラス、関数、キーワードを調べて、それらが何であるか、何を実行し、何を知っているかを知る機能) と eval を組み合わせた機能を提供しています。今日マクロという用語を知っているほとんどのプログラマーは、CまたはC ++でマクロに遭遇しており、そこではマクロは言語の小さなサブセットに組み込まれた静的機能であり、プログラムのテキストの文字列置換のみが可能です。ただし、動的言語では、マクロはコンパイラの内部動作へのアクセスと、インタープリタ、仮想マシン、またはランタイムへの完全なアクセスを提供し、コードを最適化したり、言語の構文や文法を変更したりできる言語のような構造を定義できます。
アセンブリ、C、C++、初期のJava、およびFortran は、通常、このカテゴリには当てはまりません。[説明が必要]
最も初期の動的プログラミング言語はLisp(McCarthy、1965)と考えられており、これは今日までプログラミング言語の設計に影響を与え続けています。[2]
次の例は、Common Lisp言語とそのCommon Lisp オブジェクト システム(CLOS) を使用した動的機能を示しています。
この例では、計算されたソースコードから実行時に関数を変更する方法を示しています。
; ソースコードは変数
CL-USERにデータとして保存されます> ( defparameter *best-guess-formula* ' ( lambda ( x ) ( * x x 2.5 ))) *BEST-GUESS-FORMULA*
; コードから関数が作成され、実行時にコンパイルされると、関数は best-guess という名前で利用可能になります
CL-USER > ( compile 'best-guess *best-guess-formula* ) # <Function 15 40600152F4>
; 関数は
CL-USER > ( best-guess 10.3 ) 265.225と呼ばれる
; ソースコードは実行時に改善される可能性があります
CL-USER > ( setf *best-guess-formula* ` ( lambda ( x ) , ( list 'sqrt ( third *best-guess-formula* )))) ( LAMBDA ( X ) ( SQRT ( * X X 2.5 )))
; 関数の新しいバージョンがコンパイルされています
CL-USER > ( compile 'best-guess *best-guess-formula* ) # <Function 16 406000085C>
; 次の呼び出しは、遅延バインディングの機能である新しい関数を呼び出します
CL-USER > ( best-guess 10.3 ) 16.28573
この例では、クラスが変更されたときに既存のインスタンスを変更して新しいスロットを含める方法と、既存のメソッドを新しいバージョンに置き換える方法を示します。
; 人クラス。人には名前があります。CL
-USER > ( defclass person () (( name :initarg :name ))) # <STANDARD-CLASS PERSON 4020081FB3>
; クラス person のオブジェクト用のカスタム印刷メソッド
CL-USER > ( defmethod print-object (( p person ) stream ) ( print-unreadable-object ( p stream :type t ) ( format stream "~a" ( slot-value p 'name )))) # <STANDARD-METHOD PRINT-OBJECT NIL ( PERSON T ) 4020066E5B>
; 人物インスタンスの一例
CL-USER > ( setf *person-1* ( make-instance 'person :name "Eva Luator" )) # <PERSON Eva Luator>
; personクラスは2番目のスロットを取得します。そして、nameとageというスロットを持ちます。CL
-USER > ( defclass person () (( name :initarg :name ) ( age :initarg :age :initform :unknown ))) # <STANDARD-CLASS PERSON 4220333E23>
; オブジェクトを印刷するためのメソッドを更新
CL-USER > ( defmethod print-object (( p person ) stream ) ( print-unreadable-object ( p stream :type t ) ( format stream "~a age: ~" ( slot-value p 'name ) ( slot-value p 'age )))) # <STANDARD-METHOD PRINT-OBJECT NIL ( PERSON T ) 402022ADE3>
; 既存のオブジェクトが変更され、追加のスロットと新しい印刷メソッドが追加されました
CL-USER > *person-1* # <PERSON Eva Luator age: UNKNOWN>
; インスタンスCL-USERの新しい年齢スロットを設定できます> ( setf ( slot-value *person-1* 'age ) 25 ) 25
; オブジェクトが更新されました
CL-USER > *person-1* # <PERSON Eva Luator age: 25>
次の例では、クラスpersonに新しいスーパークラスが追加されます。printメソッドは再定義され、複数のメソッドを有効なメソッドにアセンブルします。有効なメソッドは、引数のクラスと実行時に利用可能かつ適用可能なメソッドに基づいてアセンブルされます。
; クラス person
CL-USER > ( defclass person () (( name :initarg :name ))) # <STANDARD-CLASS PERSON 4220333E23>
; 人物は名前だけを出力します
CL-USER > ( defmethod print-object (( p person ) stream ) ( print-unreadable-object ( p stream :type t ) ( format stream "~a" ( slot-value p 'name )))) # <STANDARD-METHOD PRINT-OBJECT NIL ( PERSON T ) 40200605AB>
; 人インスタンス
CL-USER > ( defparameter *person-1* ( make-instance 'person :name "Eva Luator" )) *PERSON-1*
; 人物インスタンスを表示
CL-USER > *person-1* # <PERSON Eva Luator>
; プリントメソッドを拡張可能に再定義します
。around メソッドはプリントメソッドのコンテキストを作成し、次のメソッドを呼び出します。CL
-USER > ( defmethod print-object :around (( p person ) stream ) ( print-unreadable-object ( p stream :type t ) ( call-next-method ))) # <STANDARD-METHOD PRINT-OBJECT ( :AROUND ) ( PERSON T ) 4020263743>
; プライマリメソッドは名前
CL-USERを出力します> ( defmethod print-object (( p person ) stream ) ( format stream "~a" ( slot-value p 'name ))) # <STANDARD-METHOD PRINT-OBJECT NIL ( PERSON T ) 40202646BB>
; 新しいクラス id-mixin は id を提供します
CL-USER > ( defclass id-mixin () (( id :initarg :id ))) # <STANDARD-CLASS ID-MIXIN 422034A7AB>
; printメソッドはIDスロットの値を出力するだけです
CL-USER > ( defmethod print-object :after (( object id-mixin ) stream ) ( format stream " ID: ~a" ( slot-value object 'id ))) # <STANDARD-METHOD PRINT-OBJECT ( :AFTER ) ( ID-MIXIN T ) 4020278E33>
; ここで、クラス person を再定義して、mixin id-mixin
CL-USERを含めます> ( defclass person ( id-mixin ) (( name :initarg :name ))) # <STANDARD-CLASS PERSON 4220333E23>
; 既存のインスタンス *person-1* に新しいスロットが追加され、それを 42 に設定します
CL-USER > ( setf ( slot-value *person-1* 'id ) 42 ) 42
; オブジェクトを再度表示します。print-object関数に有効なメソッドが追加され、aroundメソッド、primaryメソッド、afterメソッドの3つのメソッドが呼び出されます。CL
-USER > *person-1* # <PERSON Eva Luator ID: 42>
一般的な動的プログラミング言語には、JavaScript、Python、Ruby、PHP、Lua、Perlなどがあります。一般的に動的言語と見なされるものは以下のとおりです。
(多くの人は「スクリプト言語」という用語を使用します。)