说,我想添加两个数字的向量(在数学意义上).我自然会做类似的事情:
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;
}
Run Code Online (Sandbox Code Playgroud)
嗯,没关系,但我怀疑a
并b
复制每一个电话,这不是很好.所以我宣布他们ref
.
T[] add(T)(ref T[] a, ref T[] b) { ...
Run Code Online (Sandbox Code Playgroud)
它在传递变量时效果很好,但对于测试,我使用数组实例:
assert(add([1, 2, 3], [4, 5, 6]) == [5, 7, 9]);
Run Code Online (Sandbox Code Playgroud)
这失败了,因为它无法推断出数组的引用.我已经设法找到一个解决方法:
T[] add(T)(T[] a, T[] b) {
return add(a, b);
}
Run Code Online (Sandbox Code Playgroud)
这似乎解决了这个问题,但看起来相当愚蠢.什么是我的问题更好的设计?
或者把它放在较小的问题中:我是否真的必须声明参数ref
以避免复制?可以编译器,因为我不修改,a
并b
为我优化这个?如何通过方式声明参数不可变(我尝试过immutable
关键字,看起来我使用它错了)?将res
在变通方法中真正复制两次,还是通过移动返回?
你真的应该阅读http://dlang.org/d-array-article.html.它详细介绍了D中的数组是如何工作的.但是对于简短的回答,所有在传递参数时都会被复制
T[] add(T)(T[] a, T[] b) {...}
Run Code Online (Sandbox Code Playgroud)
指针的底层指针和长度.没有复制任何元素.相反,阵列是"切片".结果内部的数组add
是add
参数的片段,这意味着它们引用的是与原始数组相同的内存.改变切片的元素将改变原始数组的元素,因为它们是相同的元素.但是,改变数组本身(例如,为其分配另一个数组或附加到它)不会影响原始数据,如果附加到数组会导致其内存被重新分配以腾出空间(或者如果为阵列分配了新数组) ),然后该数组将不再引用与原始数据相同的元素.代码中唯一一个正在制作数组副本的地方a.dup
.
数组标记的ref
作用是使它们不被切片.相反,它们是原始数组而不是切片.因此,如果有任何内容附加到本地数组或者它被重新分配,那么这将影响原始数组(如果你没有使用它将不会有ref
).
此外,ref
只接受左值(意味着值可以在赋值的左侧),并且数组文字是右值(意味着它们只能在赋值的右侧),所以你不能将它们传递给一个接受其参数的函数ref
.如果你想同时接受这两个,你要么必须不接受ref
,重载你的功能,以便你有一个ref
非ref
版本(看起来你用作你的解决方案),或者使用auto ref
而不是ref
,在这种情况下它将接受两者(但auto ref
仅适用于模板化函数,并且它基本上只是用于自己复制函数的简写,因为这是什么auto ref
一样).一般来说,如果你不想改变原作,你不应该路过ref
.
一个建议让你的代码更快:没有理由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;
}
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,您可以使用数组向量操作并完全跳过循环:
T[] add(T)(T[] a, T[] b)
{
assert(a.length == b.length);
auto res = a.dup;
res[] += b[];
return res;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果你想正确理解数组如何在D中工作,你真的应该阅读http://dlang.org/d-array-article.html.
归档时间: |
|
查看次数: |
132 次 |
最近记录: |