ダイナミック言語


ダイナミックタイプ言語ではタイプの2字を取り除くと、ダイナミック言語になります.例えば、pythonはダイナミック言語だと言われているところが少なくありませんが、実はそのダイナミック性が多いだけです.静的言語にもダイナミック性がありますが、それほど明らかではありません.そのため、ダイナミック言語は曖昧な言葉です.一般的には主流の認識、例えばpythonです.rubyは動的言語、C、javaは静的言語など
 
動的性は言語を修飾するのに適していないで、いくつかの文法の特性を修飾するのに適して、簡単に言えば、静的はコンパイル期間が決定することができるいくつかの事で、動的は運行期間がやっと決定する事で、例えばC++で、普通の関数の呼び出しはコンパイル期間が決定するので、虚関数の呼び出しは運行期間が決定するので、コンパイル期間は1段の調査表のコードを出力することしかできません具体的にどの関数が呼び出されるかは,実行時の状況によって決まるが,javaのベースクラスから派生クラスへのタイプ変換,instanceofなどがあるが,これらの操作はそれほど時間がかからない.pythonのダイナミック性は、ダイナミックタイプ、リアルタイム増減変数やプロパティなど、より柔軟になります.
 
多くのダイナミック言語のダイナミックな表現は、ほとんどのメカニズムが実行期間に実現されており、コンパイル期間にとってコードをバイトコードに変換するだけで、バイトコードも非常に具体的な「どうすればいいか」ではなく「何をするか」を説明するだけで、pythonの例を挙げます.
 
class A: 
    pass 
A().f() 

これはもちろん間違いを報告します.A類にはfの方法がありませんが、私たちはこのようにすることができます.
 
class B: 
    def f(self): 
        print self 
A.__bases__ = (B,) 
A().f() 

BをAのベースクラスとして指定すると、最後にfを呼び出すことに成功し、A()というオブジェクトが印刷されます.この例の表現はC++とjavaでは想像できない.後者は既存のベースクラスでなければならないため、ベースクラスからサブクラスが派生しないが、pythonはオブジェクト向けのより本質的なものを示している.サブクラスはベースクラスの存在に依存する必要はなく、実際には自分をパラメータとしてベースクラスのインタフェースを呼び出したいだけだ.(またはデータにアクセスするがpythonの実装では,オブジェクトデータ部分はサブクラスオブジェクト自体であるが,ベースクラスの静的データにアクセスでき,実際にはクラスオブジェクトの属性である)ため,サブクラスはベースクラスより先に存在し,その後ベースクラスができてからパパを認識しても遅くない(その後はいつでもパパを変えることができる)あ、サブクラスオブジェクト呼び出し方法の場合は、まず自分のところで探して、見つからない場合はベースを探して、見つからない場合はベースのベースを探して(もしあれば)、結局見つからない場合は、エラーを報告するしかありません.実際にはC++やjavaもこのことをしていますが、静的なものにすぎず、コンパイルエラーが見つかりません
 
そのため、ダイナミック性はコンパイル期間にしなければならないことを実行期間に置いて、より強い柔軟性を実現することであり、このような代価は効率の問題であり、上記の方法は虚表よりずっと遅いと考えられています.pythonの属性は、名前と値に対応するdictで保存されているからです.例えば、私たちはこのようにすることができます.
 
class A: 
    pass 
a = A() #{} 
a.a = 123 #{"a" : 123} 
a.b = 456 #{"a" : 123, "b" : 456} 
del a.a #{"b" : 456} 

動的にオブジェクトに属性を増減し、実現メカニズムは注釈書きであり、aオブジェクトは自分でdictを維持し、keyは変数名であり、valueは変数の値であり、これにより、getattr(a、「cc」)のような変数名文字列に基づいてリアルタイムで対応する値を見つけることができる.あ、a.ccに相当します.だからpythonでオブジェクト属性にアクセスするのは、文字列を持ってhashテーブルに行って調べることです.調べられなければ間違いを報告します.自然に文字列hashと比較に関連しています
 
質問ですが、文字列が最も一般的なアルゴリズムは多くの人が書くことができます.多分このようなものです(Cコード):
 
for (int i = 0; a[i] || b[i]; ++ i) 
{ 
    if (a[i] != b[i]) 
    { 
        return a[i] - b[i]; 
    } 
} 
return 0; 

今、このアルゴリズムの時間の複雑さはいくらですか.
 
私は多くの人にこの質問をしたことがあります.80%以上がO(N)と言っています.この答えは間違いではありませんが、すべて正しいわけではありません.実際には、最悪の場合はO(N)、平均はO(1)!平均の証明は以下の通りです.
 
aとbの2つの文字列があり、各文字の取値がcである可能性があると仮定すると、ランダムに入力すると、上記のアルゴリズムの過程である1対の文字に対して、1/cの確率が等しいだけで、次のサイクルに入り、(c-1)/cの確率が直接戻るので、比較して1回で戻る確率は(c-1)/cであり、比較して2回で戻る確率は(c-1)/c^2......比較k回の戻り確率は(c-1)/c^kであり,これは収束級数であるため,比較回数は平均して定数,c/(c-1)に向かう.
 
また別の観点から、2文字列がランダムで長さが無限であると仮定し、比較回数の期待値をEとすると、第1対の文字が等しい場合、第2対の文字からの比較回数の期待もEであり、方程式E=(c-1)/c+(E+1)/cがあり、E=c/(c-1)
 
しかし、pythonの変数名に戻ってこの問題を検索し、コードにバグがない場合は、ほとんどのプロパティがアクセスするたびにプロパティが存在するため、pythonはここで2つの最適化を行いました.
 
一、文字列のhash値は一度しか計算されず、文字列オブジェクトのヘッダ構造に保存されるので、計算を繰り返すたびに省くことができます.pythonの文字列は可変オブジェクトなので、そうすることができます.
 
二、属性関連の文字列はinternメカニズムがあり、簡単に言えば文字列表を維持し、一部の文字列に対して、そのグローバル一意性を保証する.そうすればstrcmpを使わず、直接2つの文字列オブジェクトのアドレスを比較すればよい.アドレスが同じであれば、内容は間違いなく同じである.メモリであるため、もちろんアドレスが異なる内容は必ずしも異なるとは限らない.アドレス、アドレスが異なりstrcmpが呼び出され、実行時に動的に修正され、実行速度が徐々に加速し、コード自体のダイナミック性が強くなければ(getattrのようなものを少なく使う)、strcmpはほとんど使われません
 
しかし、このようにするとhashテーブル操作があり、速度的には静的タイプの言語に及ばない.
 
また特筆すべきは局所変数であり、グローバル変数はモジュールの属性と見なすことができるため、実現方式は上記と同じであり、局所変数はさらに最適化されている.一つの関数にとって、外部から直接その局所変数を引用することはできないため、内部はtupleに最適化されている.これは特例であり、コンパイル時に区別できるを選択します.実際の状況に応じて名前を探すわけではありません.たとえば、次のようなものです.
 
i = 456 
def f(): 
    print i 
    i = 123 
    print i 

最初のprintはエラーを実行します.このiはローカル変数であるため、このときはまだ値を付けていません.多くの初心者が考えているようにグローバルなiを印刷してからローカルを印刷するわけではありません.ここでは効率のためにダイナミック性を犠牲にします(あるいは、著者はこのダイナミック性は必要ないと考えています).