2013-10-06 47 views
2

说,我想添加两个向量(在数学意义上)的数值。当然,我会做一些事情,如:了解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; 
} 

嗯,这是好的,但我怀疑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,以避免复制?难道编译器,因为我不修改ab,为我优化?我如何声明不变的方式(我试过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只接受左值(意味着可以在赋值左侧进行的值),而数组文字是右值(意味着它们只能在赋值的右侧进行),所以你不能将它们传递给一个函数,它的参数是ref。如果要同时接受,你要么必须不被接受ref,超载功能,让你有一个ref和非ref版本(这似乎你用什么作为您的解决方案),或使用auto ref代替ref ,在这种情况下,它会同时接受(但auto ref只适用于模板功能,基本上只是自己复制功能的简称,因为这就是auto ref所做的)。一般来说,如果你不想改变原作,你不应该通过ref

一个建议让您的代码更快:没有理由dupa然后再次循环并将其与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这似乎确实起作用,这会更好。我对数组向量操作不太熟悉,所以我不知道你能做什么,不能做什么。我尝试了一个[] + b []',这并没有奏效,并放弃了它。但是由于'res + = b []'有效,我会相应地更新我的答案。 –