当我链接迭代器而不是收集到临时HashSet时,为什么会得到不一致的结果?

avy*_*avy 0 iterator hashset rust data-structures

我正在写一个Rust函数,它接受一个数字列表和一个最大值,并将给定数字的所有倍数加到最大值(重复数只计算一次).我写的函数的第一个版本是

use std::collections::HashSet;

pub fn sum_of_multiples(limit: u32, factors: &[u32]) -> u32 {
    let set: HashSet<u32> = factors
        .iter()
        .map(|factor| {
            let top: u32 = (limit - 1) / factor;

            (1..=top).map(move |num| num * factor)
        }).flatten()
        .collect();

    set.iter().fold(0, |acc, num| acc + num)
}
Run Code Online (Sandbox Code Playgroud)

(我知道合并HashSets这样可能不是最好的解决方案).这给出了预期的结果:

println!("{}", sum_of_multiples(100, &[3, 5])) // 2318
Run Code Online (Sandbox Code Playgroud)

当我把呼叫collect从中间取出并连接到最后一个时fold,我得到了一个不同的答案:

pub fn sum_of_multiples(limit: u32, factors: &[u32]) -> u32 {
    let val: u32 = factors
        .iter()
        .map(|factor| {
            let top: u32 = (limit - 1) / factor;

            (1..=top).map(move |num| num * factor)
        }).flatten()
        .fold(0, |acc, num| acc + num);

    val
}
Run Code Online (Sandbox Code Playgroud)

结果:

println!("{}", sum_of_multiples(100, &[3, 5])) // 2633
Run Code Online (Sandbox Code Playgroud)

我知道迭代器是懒惰地评估的,但我假设它们按照它们被使用的顺序按顺序进行评估.这是因为行为flattenHashSetS' 我不明白为什么第二轮结果不同,或2633的重要性(如果有的话)是什么.

Sta*_*eur 5

您没有删除第二个代码段中的重复项,因为您直接使用了迭代器.

(我知道这样的合并HashSet可能不是最好的解决方案).

由于您需要临时存储来删除重复项,也许您可​​以使用a Vec然后将其放入集合中,或者不使用集合,您可以Vec自己对重复值进行排序和过滤,但这需要测试.

此外,itertools还提供了一个独特的适配器,可在内部跟踪唯一值.- E_net4

这个也应该进行测试.这使您不必担心它是如何实现的.

最后,您可以在一个表达式中编写函数:

use std::collections::HashSet;

pub fn sum_of_multiples(limit: u32, factors: &[u32]) -> u32 {
    factors
        .iter()
        .flat_map(|factor| {
            let top = (limit - 1) / factor;

            (1..=top).map(move |num| num * factor)
        })
        .collect::<HashSet<u32>>()
        .iter()
        .sum()
}
Run Code Online (Sandbox Code Playgroud)

  • 此外,`itertools`提供了一个[`unique`](https://docs.rs/itertools/0.7.8/itertools/trait.Itertools.html#method.unique)适配器,它在内部跟踪唯一值. (4认同)