D 如何在一个范围内撤消popFront

D 如何在一个范围内撤消popFront,d,range,D,Range,“撤消”popFront操作的标准方法是什么?我意识到这不可能适用于所有范围,但对于数组之类的东西,假设您有 int[] a = [ 1, 2, 3 ]; 如果您执行了a.popFront(),将a的起始指针调整为指向2,您将如何撤消该操作以返回范围内的1?我知道std.container.insertFront 我试过了 a = a[1..$]; a = a[-1..$]; 但是第二行抛出一个范围错误。此外,阵列支持切片,但我正在寻找的方法应该支持非随机访问范围和不支持切片的范围。因此,即

“撤消”popFront操作的标准方法是什么?我意识到这不可能适用于所有范围,但对于数组之类的东西,假设您有

int[] a = [ 1, 2, 3 ];
如果您执行了
a.popFront()
,将
a
的起始指针调整为指向
2
,您将如何撤消该操作以返回范围内的
1
?我知道std.container.insertFront

我试过了

a = a[1..$];
a = a[-1..$];

但是第二行抛出一个
范围错误。此外,阵列支持切片,但我正在寻找的方法应该支持非随机访问范围和不支持切片的范围。因此,即使
a[-1..$]
有效,也不能解决我的问题。

标准方法是在弹出前保存范围的副本。弹出是一种破坏性的变异,范围可以自由地取消分配元素、重新平衡基础树或以其他方式使前一个元素无效

因此:


您不能撤消
popFront
。您甚至不能对数组执行与arr[-1..$]
等效的操作。如果你想要旧版本,你必须先保存它

auto saved = range.save;
range.popFront();
range = saved; // "undo" popFront()
阵列也不提供比这更多的功能。要对没有rangeAPI的数组执行相同的操作,您必须执行以下操作

auto saved = arr;
arr = arr[1 .. $];
arr = saved;
“撤消”范围或数组上的pop操作的唯一方法是先保存它,然后使用旧版本。range API或阵列都不提供任何其他功能。它们不保存自己的状态(因此不知道如何撤消以前的操作),甚至阵列片也不知道它们之前或之后的内存中可能有什么数据(并且尝试访问阵列之前或之后的内存将是非法的,正如您在点击
RangeError
时所看到的那样)


因此,如果你不得不担心“撤销”任意数量的元素弹出,那么你可能不得不做一些事情,比如保持原始范围,跟踪你弹出的元素数量,这样你就可以弹出元素数量减去你想要的“撤销”级别数。虽然这里可能不会进行太多的复制(对于数组,它只是指向同一内存但位于内存中不同位置的多个切片),但如果您不使用切片处理某个范围,那么所有这些弹出操作都可能非常昂贵(在每个元素弹出之前保存该范围的保存版本也是如此),特别是如果您试图一次撤销一个级别,那么range API可能不太适合您尝试执行的操作,您可能需要重新考虑如何执行该操作。

这似乎不方便。如果我需要从0返回到n个元素,如果范围不是随机访问,我必须保存n个范围。@Johm然后首先将其复制到数组中。请注意,严格来说,不是复制数组,而是复制指向数组的指针。换句话说,更改第二个范围中的值将更改第一个范围中的值。没有重复发生。我想在您的回答中,“随机访问”范围应该替换为“双向范围”,不是吗?@МаааССаааааааааааа107。如果跟踪要取消映射的元素数量,则可以从原始元素中获取一个新的片段,其中包含要在O(1)中添加的元素,而如果没有,则必须逐个地弹出所有元素,因此将是O(n)。但我不知道双向范围会如何影响到这一点,因为我们讨论的是撤销前端的弹出元素。从后面弹出元素对你没有任何帮助。哦,糟糕,我确信双向范围是随机访问范围,具有O(n)元素访问(不是从两端弹出,而是从前面移动的两个方向)。这个名字有点误导人,但我感到羞耻。
auto saved = arr;
arr = arr[1 .. $];
arr = saved;