专用于内置数组的范围原语消耗它们的源,但可以轻松设计一个范围系统,而该系统宁愿基于 .ptr
来源(乍一看更灵活)。
struct ArrayRange(T)
{
private T* _front;
private T* _back;
this(ref T[] stuff) {
_front = stuff.ptr;
_back = _front + stuff.length;
}
@property bool empty(){return _front == _back;}
@property T front(){ return *_front;}
@property T back(){return *_back;}
void popFront(){ ++_front;}
void popBack(){--_back;}
T[] array(){return _front[0 .. _back - _front];}
typeof(this) save() {
auto r = this.array.dup;
return typeof(this)(r);
}
}
void main(string[] args)
{
auto s = "bla".dup;
// here the source is 'fire-proofed'
auto rng = ArrayRange!char(s);
rng.popFront;
assert (s == "bla");
assert (rng.array == "la");
// default primitives: now the source is consumed
import std.array;
s.popFront;
assert (s == "la");
}
为什么默认系统不基于指针算法,因为弹出前端意味着重新分配/降低效率?
这种设计有什么理由吗?
最佳答案
我同意你的看法,没有理由在每个 popFront
重新分配.好在那不是那么回事!popFront
机制与您提供的非常相似,并且不消耗源,仅消耗您调用的数组 popFront
(因为,它毕竟是流行音乐)。您实现的是切片数组时发生的情况:您获得原始数组的引用范围:
auto a = [1, 2, 3];
auto s = a[];
s.popFront;
assert(s == [2, 3]);
assert(a == [1, 2, 3]);
.dup
是否提供数组的副本,以便您可以安全地修改副本而不更改原始数组,因此它复制原始数组,然后为该副本提供输入范围。当然你可以修改副本(这就是重点),还有popFront
会改变它,但仍然使用指针算法而不改变源。auto a = [1, 2, 3];
auto s = a.dup;
s.popFront;
assert(s = [2, 3]);
assert(a = [1, 2, 3]);
.dup
可能看起来不是很有用,因为我们将它与数组一起使用,但是在处理“纯”范围时它非常重要,因为范围通常懒惰不使用它。由于消耗了范围的副本而不是初始副本,因此我们可以安全地将此副本传递给函数,并且仍然保持初始惰性范围完好无损。
关于range - 为什么数组范围基元消耗它们的源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32177415/