2

基礎となる数値型を交換できるような方法で代数式を定式化したいと考えています。必要に応じて、複素数、大整数、行列などについて考えてみてください。このため、の代わりにadd(a, b)orと書きます。静的に型付けされた言語では、単純に関数の型ベースのオーバーロードを使用して、さまざまな代替手段を実装します。しかし、JavaScript ではこれが機能しないため、代替手段を探しています。実行されるメソッドは、両方のオペランドの型によって異なります。a.add(b)a + badd

私が思いついた方法の 1 つは、次の二重ディスパッチメカニズムです。

  1. のように式を書きa.add(b)ます。

  2. 次の方法で、特定のタイプ (たとえば、独自のComplexタイプ、または組み込みのNumberタイプ) に対してそのメソッドを実装します。

    add: function(that) { that.addComplex(this); }
    

    したがって、2 番目の呼び出しのメソッド名は、オペランドの 1 つの型をエンコードします。

  3. すべての組み合わせを処理する特殊なメソッドを実装します。たとえば、

    Number.prototype.addComplex = function(that)
      { return newComplex(that.real + this, that.imaginary); }
    

すべての型を知っていると仮定して、すべての組み合わせを確実に処理できるようにします。私が今困っているのは、これらのオブジェクトの作成です。

上記のアプローチは、仮想メソッドのディスパッチに大きく依存しているため、私が見る方法では、ある種の継承が必要です。従来のコンストラクタ関数では問題ありませんが、先ほど行ったこの jsperfによると、コンストラクタ関数を使用したオブジェクトの作成は、オブジェクト リテラルよりも遅くなる傾向があります。この例の Firefox の場合のように、非常に大きな要因で遅くなることがあります。したがって、演算子のオーバーロードを機能させるためだけに、複素数値の数値中間体などのすべてに対してこの種のオーバーヘッドが発生するのは気が進まない。

この jsperf で試したもう 1 つのアプローチは、プロトタイプを使用するのではなく、仮想メソッドを各オブジェクト インスタンスのプロパティとして格納することです。テスト済みのほとんどすべてのブラウザーで非常に高速に動作しますが、ここではオブジェクトのサイズが心配です。2 つの実際の浮動小数点値を持つオブジェクトを持つことを心配していますが、演算子のオーバーロードのすべてのペアを処理するためだけに、おそらく 50 もの異なるメンバー関数があります。

add3 番目のアプローチは、何らかの方法で引数の型を検査し、それに基づいて決定を下す単一の関数を持つことです。いくつかの数値型識別子の組み合わせによってインデックス付けされたリストで実際の実装を検索する可能性があります。私はまだテスト用にこれを書いていませんが、この種の型チェックはかなり遅く感じます。また、JIT コンパイラがこの風変わりな種類の関数ディスパッチを最適化できるかどうかも疑問です。

現在の JavaScript 実装をだまして、オブジェクトを適切に最適化された二重ディスパッチにさせる方法はありますか?

4

1 に答える 1

2

3 番目のアプローチは非常に実行可能に見えます。

function Complex(re, im) {
    return {type:'c', re:re, im:im }
}
function Real(n) {
    return {type:'r', n:n }
}

funcs = {
    add_c_r: function(a, b) {
        console.log('add compl to real')
    },
    add_r_c: function(a, b) {
        console.log('add real to compl')
    }
}

function add(a, b) {
    return funcs["add_" + a.type + "_" + b.type](a, b);
}

add(Complex(1, 2), Real(5))
add(Real(5), Complex(1, 2))

1 つの余分なフィールド + 1 つの間接化は妥当なコストです。

于 2013-11-28T13:45:10.163 に答える