2

たとえば、数値の 2 つのベクトルを (数学的な意味で) 加算したいとします。当然、私は次のようなことをします:

T[] add(T)(T[] a, T[] b) {
    assert(a.length == b.length);
    T[] res = a.dup;
    foreach (i; 0 .. a.length) {
        res[i] = a[i] + b[i];
    }
   return res;
}

まあ、大丈夫ですが、すべての呼び出しを疑っabコピーしますが、これはそれほど素晴らしいことではありません。だから私はそれらを宣言しますref

T[] add(T)(ref T[] a, ref T[] b) { ...

変数を渡している間はうまく機能しますが、テストでは配列インスタンスを使用します。

assert(add([1, 2, 3], [4, 5, 6]) == [5, 7, 9]);

そして、配列の参照を推測できないため、これは失敗します。私は回避策を見つけることができました:

T[] add(T)(T[] a, T[] b) {
    return add(a, b);
}

問題を解決しているように見えますが、かなりばかげています。私の問題に適した設計は何ですか?

refまたは、それをより小さな質問に入れます:コピーを避けるために引数を宣言する必要は本当にありますか? コンパイラーは、私が変更aしたりb、これを最適化したりしないので、私のためにこれを最適化できますか? ちなみに、引数を不変と宣言するにはどうすればよいですか(immutableキーワードを試しましたが、間違って使用しているようです)?res回避策で本当に 2 回コピーされますか、それとも移動によって返品されますか?

4

1 に答える 1

5

http://dlang.org/d-array-article.htmlを読む必要があります。D の配列がどのように機能するかについて詳しく説明します。しかし、簡単な答えとして、引数を渡すときにすべてがコピーされます

T[] add(T)(T[] a, T[] b) {...}

ポインターの基になるポインターと長さです。どの要素もコピーされません。むしろ、配列は「スライス」されます。の内部で結果として得られる配列は、 の引数のaddスライスですadd。つまり、元の配列と同じメモリを参照します。スライスの要素を変更すると、元の配列の要素が変更されます。これらは同じ要素であるためです。ただし、配列自体を変更しても (たとえば、別の配列を割り当てたり、追加したり) 、元の配列には影響しません。また、配列に追加した結果、そのメモリが再割り当てされて空きができた場合 (または新しい配列が配列に割り当てられた場合) は、元の配列には影響しません。 )、その配列は元の要素と同じ要素を参照しなくなります。配列のコピーが作成されるコード内の唯一の場所はa.dup.

配列をマークするrefことは、それらがスライスされないようにすることです。むしろ、スライスでなく元の配列です。そのため、ローカル配列に何かが追加されたり、再割り当てされたりすると、元の配列に影響します (ただし、 を使用していなければ影響はありませんref)。

また、ref左辺値 (割り当ての左側に配置できる値を意味する) のみを受け入れ、配列リテラルは右辺値 (割り当ての右側にのみ配置できることを意味する) であるため、で引数を取る関数にそれらを渡しますref。両方を受け入れたい場合は、 by を受け入れないかref、関数をオーバーロードして arefと非refバージョン (ソリューションとして使用したものに表示されます) を持つか、auto ref代わりに をref使用する必要があります。 '両方を受け入れます(ただしauto ref、テンプレート化された関数でのみ機能します。基本的には、関数を自分で複製するための省略形です。auto refします)。ただし、一般的には、オリジナルを変更したくない場合は、通り過ぎてはいけませんref

コードを高速化するための 1 つの提案:dup a再度ループして、 と一緒に追加する理由はありませんb。それがあなたのやりたいことなら、+=もっと

T[] add(T)(T[] a, T[] b)
{
    assert(a.length == b.length);
    auto res = a.dup;
    foreach (i; 0 .. a.length)
        res[i] += b[i];
   return res;
}

または、配列ベクトル操作を使用して、ループを完全にスキップすることもできます。

T[] add(T)(T[] a, T[] b)
{
    assert(a.length == b.length);
    auto res = a.dup;
    res[] += b[];
    return res;
}

ただし、D で配列がどのように機能するかを正しく理解したい場合は、 http://dlang.org/d-array-article.htmlを読む必要があります。

于 2013-10-06T09:09:51.023 に答える