【问题标题】:How to change the index of a vector element?如何更改向量元素的索引?
【发布时间】:2020-06-02 01:07:08
【问题描述】:

在 JavaScript 中我会这样做:

function move(arr, old_index, new_index) {
  while (old_index < 0) {
    old_index += arr.length;
  }
  while (new_index < 0) {
    new_index += arr.length;
  }
  if (new_index >= arr.length) {
    var k = new_index - arr.length;
    while ((k--) + 1) {
      arr.push(undefined);
    }
  }
  arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
  return arr;
}

如何在 Rust 中完成同样的事情?

我不想使用insert 和remove,因为我的向量是std::vec::Vec&lt;std::string::String&gt;,我想将它们从字面上移到向量中的不同位置,而不是删除它们然后插入一个副本。

我不想交换 2 个元素。我想将元素的索引更改为任意其他索引,就像一个人切到队列中的任意其他位置一样。

【问题讨论】:

  • 分享您的研究对每个人都有帮助。告诉我们您尝试了什么以及为什么它不能满足您的需求。这表明您已经花时间尝试帮助自己,它使我们免于重复明显的答案,最重要的是它可以帮助您获得更具体和相关的答案。另见:How to Ask
  • @GirkovArpa:元素存储在向量中的特定内存位置,向量是非常低的特征集合;索引是从连续分配的内存开始的特定偏移量。如果不将元素移动到新的内存位置,则无法更改元素的索引。充其量您可以使向量存储指向在其他地方分配的元素的指针,并交换指针(它仍然移动内存,但不是整个元素)。你在 JavaScript 中所做的仍然是移动引用,它并没有神奇地改变未移动元素的索引。
  • Strings 已经是堆分配的并且移动起来很便宜。无需使用&amp;String 添加间接级别。
  • Rust 没有像 JavaScript 那样的undefined。你不能有一个带有“洞”的Vec。也许Should I store unordered values from a series with large holes in a sparse Vec or HashMap in Rust? 会回答你的问题。你可能只想要一个HashMap&lt;SomeNumberType, T&gt;。

标签: vector rust


【解决方案1】:

当您执行 insert + remove(或 JavaScript 中的双重 splice)时,您会将所有项目在两个索引中较大的一个和数组末尾之间移动两次:首先将它们移回一个remove 的插槽,然后将它们向前移动一个插槽,用于insert。但这是不必要的。相反,您可以简单地截取Vec 的一部分并旋转它:

fn move_me(arr: &mut [String], old_index: usize, new_index: usize) {
    if old_index < new_index {
        arr[old_index..=new_index].rotate_left(1);
    } else {
        arr[new_index..=old_index].rotate_right(1);
    }
}

请注意,此更改允许move_me 使用&amp;mut [String] 而不是&amp;mut Vec&lt;String&gt;,这使此代码更通用且更高效。它是better to accept &amp;[T] instead of &amp;Vec&lt;T&gt;,在这种情况下,同样的逻辑适用于&amp;mut Vec&lt;T&gt;,因为move_me 不需要增大或缩小向量。

另外,与另一个答案一样,我省略了从切片后面计算负索引的部分,以及当索引太大时增长向量的部分,因为这些约定都不常见在惯用的 Rust 中。

【讨论】:

    【解决方案2】:

    感谢 SCappella 告诉我 JavaScript Array.splice() 与 Rust Vec.insert() 和 Vec.remove() 做同样的事情。所以我只是继续尽可能地移植该功能。

    感谢John Kugelman 让我知道我可以删除除最后两行之外的所有内容。

    /* move is a reserved identifier */
    fn move_(arr: &mut Vec<String>, old_index: usize, new_index: usize) {
        let removed = arr.remove(old_index);
        arr.insert(new_index, removed);
    }
    

    【讨论】:

    • 处理负数和超出范围的索引不需要移植。改变索引参数是不好的风格。我会放弃所有这些(真的,除了最后两行之外的所有内容)。
    猜你喜欢
    • 1970-01-01
    • 2011-02-07
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多