[英]In-place modification, insertion or removal in the same function for hash maps in Rust
Say I have a hash map m: HashMap<K, V>
, a key k: K
and a value v: V
, and would like to do the following:假设我有一个 hash map
m: HashMap<K, V>
,一个键k: K
和一个值v: V
,并且想做以下事情:
m
does not contain a value at index k
, insert v
at index k
.m
在索引k
处不包含值,则在索引k
处插入v
。m
contains a value w
at index k
, apply a function fn combine(x: V, y: V) -> Option<V>
to v
and w
, and:m
在索引k
处包含值w
,则将 function fn combine(x: V, y: V) -> Option<V>
应用于v
和w
,并且:
None
, remove the entry at index k
from m
.None
,则从m
中删除索引k
处的条目。Some(u)
, replace the value at index k
by u
.Some(u)
,将索引k
处的值替换为u
。 Is there a way to do this "in-place", without calling functions that access, modify or remove the value at k
multiple times?有没有办法“就地”执行此操作,而无需调用多次访问、修改或删除
k
处的值的函数?
I would also like to avoid copying data, so ideally one shouldn't need to clone v
to feed the clones into insert
and combine
separately.我还想避免复制数据,所以理想情况下不需要克隆
v
来将克隆提供给insert
并单独combine
。
I could rewrite combine
to use (mutable) references (or inline it), but the wish of not copying data still remains.我可以重写
combine
以使用(可变)引用(或内联它),但不复制数据的愿望仍然存在。
Digging deeper into the Entry
documentation , I noticed that the variants of the Entry
enum offer functions to modify, remove or insert entries in-place.深入研究
Entry
文档,我注意到Entry
枚举的变体提供了就地修改、删除或插入条目的功能。
After taking std::collections::hash_map::Entry
into scope, one could do the following:在采取
std::collections::hash_map::Entry
进入 scope 之后,可以执行以下操作:
match m.entry(k) {
Entry::Occupied(mut oe) => {
let w = oe.get_mut();
match combine(v, w) {
Some(u) => { *w = u; },
None => { oe.remove_entry(); },
}
},
Entry::Vacant(ve) => { ve.insert(v); },
}
( Here is a PoC in the Rust playground.) ( 这是 Rust 操场上的 PoC。)
This, however, requires combine
to take a (mutable) reference as its second argument (which is fine in my case).但是,这需要
combine
将(可变)引用作为其第二个参数(在我的情况下这很好)。
I managed to do it in one access, one write and one key-deletion in total in the worst case.在最坏的情况下,我设法通过一次访问、一次写入和一次删除密钥来完成。 The last key-deletion should not be necessary, but I'm not certain it can be done.
最后一个键删除应该不是必需的,但我不确定它是否可以完成。 I gave it my best so far.
到目前为止,我已经尽力了。 I hope this helps!
我希望这有帮助!
Okay, so I think we want to use the Entry API .好的,所以我想我们想使用条目 API 。
The full method list for Entry
is here . Entry
的完整方法列表在这里。
I think we'd do it in the following order:我想我们会按照以下顺序进行:
m
contains a value w
at index k
: (two more steps)m
在索引k
处包含值w
:(再执行两步)v
at index k
.k
处插入v
。 This can be done by using .and_modify
and then .or_insert
.这可以通过使用
.and_modify
然后.or_insert
来完成。 Something like this:像这样的东西:
let map = // ... Initialize the map
// Do stuff to it
// ...
// Our important bit:
let mut delete_entry = false;
map.entry(k)
.and_modify(|w| { // If the entry exists, we modify it
let u = combine(v, w);
match u {
Some(y) => *w = y;
None => delete_entry = true;
}
}
)
.or_insert(v); // If it doesn't, we insert v
if delete_entry {
map.remove(k);
}
I don't think there's a way to do all three things without that last map.remove
access, so this is my best attempt for now.如果没有最后一个
map.remove
访问权限,我认为没有办法完成所有三件事,所以这是我目前最好的尝试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.