Rust 中的复合 `HashSet` 操作或如何在 Rust 中获得 `HashSet` 的显式差异/并集

问题描述 投票:0回答:3

我想用伪代码执行以下操作:

(a, b, c) = (HashSet(...), HashSet(...), HashSet(...))

(a, b, c) = (a - b - c, b - a - c, c - a - b)

在 Rust 中我尝试了这样的事情:

fn get_random_set(...) -> HashSet<String> {
    ...
}

// Sets of randomly generated "words" that define behavior of the whole program.
let action_plus: HashSet<String> = get_random_set();
let action_minus: HashSet<String> = get_random_set();
let action_new_line: HashSet<String> = get_random_set();

现在我们要从这些

HashSet
中排除所有常见的“单词”。 我了解到
difference
union
方法返回
Difference
Union
迭代器。如果我这样做:

let action_plus = HashSet::from(action_minus.union(&action_new_line).collect::<Vec<String>>());

我收到这个:

the trait `From<Vec<String>>` is not implemented for `HashSet<_, _>`

如何处理这个问题?

rust hashset
3个回答
4
投票

使用

impl FromIterator for HashSet
直接收集到
HashSet
,而不是 Vec

即:

let action_plus: HashSet<_> = action_minus.union(&action_new_line).collect();

正如 Chayim 在评论中所说,您也可以使用

|
(
BitOr
) 运算符,因为
HashSet
实现了它 并且正如文档所说:

返回 self 和 rhs 的并集作为新的 HashSet

如果您查看它的实现,您会发现它与上面的代码基本相同(尽管它从这些集合中克隆了项目):

impl<T, S> BitOr<&HashSet<T, S>> for &HashSet<T, S>
where
    T: Eq + Hash + Clone,
    S: BuildHasher + Default,
{
    type Output = HashSet<T, S>;

    fn bitor(self, rhs: &HashSet<T, S>) -> HashSet<T, S> {
        self.union(rhs).cloned().collect()
    }
}

HasSet
还实现了 BitAnd (
&
) 运算符用于创建交集,Sub (
-
) 用于创建集合差值和 BitXor (
^
) 用于创建对称差值。

您可以使用这些便利运算符,但是当人们没有想到它们时,它们可能会令人反感,因此请小心使用它们。


2
投票

您可以直接收集到

HashSet

let a = get_random_set();
let b = get_random_set();
let c = get_random_set();
let a_minus_b_minus_c = a
    .difference(&b)
    .cloned()
    .collect::<HashSet<_>>()
    .difference(&c)
    .cloned()
    .collect::<HashSet<_>>();
将输出从

cloned

 转换为 
HashSet<&String>
 需要 
HashSet<String>
,因为
difference
需要相同类型的集合。如果您同意
cloned
a_minus_b_minus_c
,则不需要最后一个
HashSet<&String>

但是,仅使用减法运算符会更容易。

HashSet
Sub
的实现,它通过引用获取两个操作数,因此每次都需要添加引用。

let a_minus_b_minus_c = &(&a - &b) - &c;

还有:

请注意,所有这些都会克隆集合中的值并返回一个新集合,因此它们的效率不是最佳的。如果您需要更高的性能,您可以通过使用

retain
(差异、交集)或
extend
(并集)修改第一个集合来手动执行操作,或者手动创建一个包含引用的新集合。


1
投票

您的目标是从所有集合中删除多个集合中的任何元素。由于您只是删除元素,因此不需要克隆任何字符串,并且可以就地执行所有操作。如果您的套装名为

a
b
c
,您可以使用以下代码:

a.retain(|x| b.remove(x) | c.remove(x));
b.retain(|x| c.remove(x));

这将首先从两个集合中分别删除

a
b
之间的交集以及
a
c
之间的交集中的所有元素,然后分别删除
b
c
之间的交集中的所有元素。总的来说,这应该比其他答案中讨论的方法快得多(尽管我没有做任何基准测试)。

请注意,第一行使用非短路

|
运算符而不是
||
,这一点很重要。最后一个操作数
c.remove(x)
显然有副作用,所以我们不能跳过它。否则,所有三个集合中的元素都不会从
c
中删除。

© www.soinside.com 2019 - 2024. All rights reserved.