【发布时间】:2021-12-18 12:20:07
【问题描述】:
在大多数语言中,对容器进行迭代并同时对其进行变异是一种明显的反模式。在 Rust 中,借用检查器使它不可能。
但在某些情况下需要这样做。一个例子是 Earley 的解析算法。我是 Rust 的新手,我不知道在扩展 HashSet (或者实际上是任何容器)时有一种已知的好方法。我想出了以下内容(从 Earley 用例概括而来):
use std::collections::HashSet;
fn extend_from_within<T, F>(original: &mut HashSet<T>, process: F)
where T: std::cmp::Eq,
T: std::hash::Hash,
F: Fn(&T) -> Set<T>
{
let mut curr : HashSet<T> = HashSet::new(); // Items currently being processed
let mut next : HashSet<T> = HashSet::new(); // New items
// go over the original set once
let hack : &HashSet<T> = original; // &mut HashSet is not an iterator
for x in hack {
for y in process(x) {
if !original.contains(&y) {
curr.insert(y);
}
}
}
// Process and extend until no new items emerge
while !curr.is_empty() {
for x in &curr {
for y in process(x) {
// make sure that no item is processed twice
// the check on original is redundant, but might save space
if !curr.contains(&y) && !original.contains(&y) {
next.insert(y);
}
}
}
original.extend(curr.drain());
std::mem::swap(&mut curr, &mut next);
}
}
如您所见,任何对 process 的调用都可以产生多个项目。它们都被添加到集合中,并且它们都必须被处理,但前提是它们还没有被看到。这工作得很好。但是保持最多 4 组,对每个项目进行 3 次成员资格检查(其中一个在原始数组上两次)对于这个问题来说似乎很荒谬。有没有更好的办法?
【问题讨论】:
-
你能准确地说出你想要什么样的突变吗?值的突变 ?条目删除 ?条目添加?
-
条目添加。
-
哈希集,无论是什么语言,在结构上都不适合混合迭代和加法,因为没有不被加法破坏的内在顺序。您似乎从错误的集合(或错误的内容)开始
-
我明白了。我根本不致力于散列集,但我需要独特的元素。具体来说,I' 使用四个使用整数的结构作为项目(集合的成员),以防万一。还有哪些其他选择?已排序的向量?
-
我不会深入研究,因为我没有时间研究算法,但由于您的结构可以复制,您可能只需结合一个哈希集来检查唯一性和一个 vecdeque 充当一个队列。拥有 Copy 元素可以让这里的一切变得更轻松,而无需一个独特的集合。
标签: parsing rust containers hashset