如何在D语言中从数组中删除一个元素

14

在D语言中,将元素x连接到数组items中非常容易,就像它是一个数组列表一样:

arr ~= x;

但是我该如何从items中删除索引为i的元素呢?

(注意:如果我删除一个元素然后添加一个新元素,数组不能被重新分配。因此,简单的切片不起作用。)


更新:

根据CyberShadow关于使用assumeSafeAppend的答案,我编写了以下代码:

static void removeAt(T)(ref T[] arr, size_t index)
{
    foreach (i, ref item; arr[index .. $ - 1])
        item = arr[i + 1];
    arr = arr[0 .. $ - 1];
    arr.assumeSafeAppend();
}

然而,当你有类似以下内容时就会出现问题:
auto superArr = [0, 1, 2, 3, 4]; //Must not be modified
auto arr = superArr[0 .. $ - 1];
writeln(superArr);
arr.removeAt(0);    //Should copy the slice and modify the copy
writeln(superArr);  //but obviously doesn't

如果从切片中删除一个元素,则不应修改其基本数组;而是需要复制该切片。

但是我无法确定数组是否是更大数组的一部分...所以这不起作用。

有什么建议吗?


根据您的编辑更新了我的答案。 - Vladimir Panteleev
@CyberShadow:这个方法有点取巧,但我还是会用的。xD 谢谢! - user541686
这可能不是我该问的地方,但你确定从设计角度来看这是正确的吗?检查T[]是否是更大数组的切片并不能百分之百确保你没有覆盖其他引用指向的数据。例如,即使你确保遵循切片=别名的约定,使用你的代码的算法可能会传递arr[i..j],其中i==0且j==arr.length,并且removeAt也不会有任何警觉。你可能需要研究一下D语言的const-correctness功能以控制别名。 - Vladimir Panteleev
@CyberShadow:嗯...但是arr[i..j]不是arr的一个切片吗?那么在删除之前不会重新分配内存吗?我不明白当j == arr.length时的问题在哪里... - user541686
1
@CyberShadow:哦,我明白了...啊,这比我想象的要丑陋得多...:\ - user541686
显示剩余3条评论
5个回答

22

在digitalmars.D上,我回答了以下问题(感谢转发):

正如其他人提到的那样,std.algorithm.remove可以帮助你。你可能特别想看看它的三个功能:(a)一次删除多个偏移量,例如remove(a, 0, 4)将删除第一个和第五个元素,(b)你可以删除子范围,例如remove(a, tuple(1, 3))将删除第二个到第四个元素,以及(c)如果你不关心删除后元素的顺序,你可以尝试使用不稳定的remove,它会执行更少的操作。

安德烈


1
谢谢你的回答!:) 不过和我在新闻组上发表的评论一样,remove 似乎并没有正确地修改数组(实际上删除任何内容仍会导致添加时重新分配内存)。:\ - user541686
你要如何移除一组索引呢?例如 'remove(a, [0, 2, 3])'? - deceleratedcaviar

12
(注意:如果我删除一个元素然后添加一个新元素,数组不得重新分配。因此,简单的切片方法是行不通的。) assumeSafeAppend函数可以告诉运行时,当向数组追加元素时,不要重新分配它(即用户确认没有其他可能被追加覆盖的切片)。 std.algorithm中的remove函数会进行原地移除。如果你使用std.container,还有Array.linearRemove函数。

1
尽管上次我检查时实现有点慢,但对于“删除”操作,我会使用 +1。因此,我经常使用 memmove 代替。 - stephan
@CyberShadow:我认为removelinearRemove不能胜任此工作(第一个会在添加新元素时导致重新分配,对吧?而第二个则无法与语言和GC集成),但我认为assumeSafeAppend非常有帮助,如果它能起作用,我会接受的。 :) +1 - user541686
@CyberShadow:不,assumeSafeAppend似乎有点棘手:它只在你从一个不是另一个数组的切片中删除时才起作用;否则,你可能会覆盖超级数组。有没有一种方法可以检测一个数组是否是更大数组的切片? - user541686
std.container.Array没有remove方法。当您提到语言/GC集成时,我不确定您在寻找什么,但是std.container.Array具有引用计数,并重载索引和切片操作符。 - Vladimir Panteleev
@CyberShadow:哦,我不知道那个。如果是这样的话,它可能会起作用,但不幸的是,它只能作为最后的手段。不过还是谢谢你! :) - user541686
显示剩余3条评论

3
如果顺序不重要,您可以将最后一个元素复制到删除位置,然后将数组长度减少一。

抱歉,但是顺序很重要。:( - user541686

2

如果您只想删除第一个或最后一个元素,请使用切片:

array = array [1..$]
array = array [0..$-1]

或者一种适用于中间值的通用方法:
array = array [0..unlucky] ~ array [unlucky+1..$]

如果元素不是基本元素,比如结构体、浮点数和整型,那么数组就会隐式地成为指针数组,这是一种高效的操作。


-6

没有自动化的方法可以做到这一点,您必须沿着数组项进行洗牌,重置 .length 然后连接。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接