2013-10-06 3 views
2

Скажите, я хочу добавить два вектора (в математическом смысле) чего-то численного. Естественно, я хотел бы сделать что-то вроде:Понимание refs в языке D

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; 
} 

Ну, это нормально, но я подозреваю, a и b копирование на каждый вызов, который не столь велика. Поэтому я объявляю их 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 быть действительно скопирован дважды в обходном пути, или возврат производится с помощью перемещения?

ответ

5

Вы действительно должны прочитать http://dlang.org/d-array-article.html. Он подробно рассказывает о том, как массивы в D работают. Но за короткий ответ, все, что получение копируются при передаче аргументов в

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

является основным указатель указателя и длиной. Ни один из элементов не копируется. Скорее, массив «нарезанный». Результирующие массивы внутри add представляют собой фрагменты аргументов add, что означает, что они относятся к той же памяти, что и исходные массивы. Мутирование элементов среза будет мутировать элементы исходного массива, потому что они являются одними и теми же элементами. Однако, мутируя сам массив (например, присваивая ему другой массив или добавляя к нему), не влияет на оригинал, и если добавление к массиву приводит к перераспределению его памяти, чтобы освободить место (или если новый массив назначен массив), то этот массив больше не будет ссылаться на те же элементы, что и у оригинала. Единственное место в вашем коде, которое копирует массив, составляет a.dup.

Какая маркировка массивов ref делает это так, чтобы они не были нарезаны. Скорее, они являются оригинальные массивы вместо ломтиков. Итак, если что-то добавлено к локальному массиву или если оно переназначено, это повлияет на исходный массив (тогда как это не было бы, если бы вы не использовали ref).

Кроме того, ref принимает только значения lvalues ​​(значения, которые могут идти в левой части задания), а литералы массива являются значениями rvalues ​​(что означает, что они могут идти только в правой части задания), поэтому вы не можете передать их функции, которая принимает свой аргумент ref.Если вы хотите принять оба варианта, вам либо не нужно принимать ref, перегрузите свою функцию, чтобы иметь версию ref и не ref (что похоже на то, что вы использовали в качестве своего решения), либо используйте auto ref вместо ref , и в этом случае он будет принимать оба (но auto ref работает только с шаблонами функций, и это в основном просто короткая рука для дублирования функции самостоятельно, потому что это то, что делает auto ref). В общем, хотя, если вы не хотите мутировать оригинал, вы не должны проходить мимо ref.

Одно предложение сделать код быстрее: нет причин для , а затем снова переверните его и добавьте его вместе с 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; 
} 

Или еще лучше, вы могли бы использовать array vector operations и пропустить цикл целиком:

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

Но опять же, вы действительно должны прочитать http://dlang.org/d-array-article.html, если вы хотите правильно понять, как работают массивы в D.

+3

не 'res [] + = b [];' также работает, чтобы исключить цикл? –

+0

@ratchetfreak Это действительно работает, что было бы еще лучше. Я не очень хорошо разбираюсь в операциях с вектором массива, поэтому я не знаю точно, что вы можете и чего не можете с ними сделать. Я попробовал 'a [] + b []', который не работал и не сдавался. Но поскольку работает 'res + = b []', я буду соответствующим образом обновлять свой ответ. –