【问题标题】:How does one get an iterator to the max value element in Rust?如何在 Rust 中获得一个指向最大值元素的迭代器?
【发布时间】:2019-09-25 16:10:19
【问题描述】:

我想访问Vec<i32> 中最大元素旁边的元素。我正在寻找这样的东西:

let v = vec![1, 3, 2];
let it = v.iter().max_element();
assert_eq!(Some(&2), it.next());

在 C++ 中,我会使用 std::max_element,然后只增加迭代器(有或没有边界检查,取决于我目前的冒险程度)。 Rust max 只返回对元素的引用,这对我的用例来说还不够好。

我想出的唯一解决方案是使用enumerate 来获取项目的索引 - 但与 C++ 方式相比,这似乎是手动且繁琐的。

我更喜欢标准库中的东西。

这个例子被简化了——我实际上想附加到最高值,然后从那个点循环整个容器(可能使用cycle() 或类似的东西)。

【问题讨论】:

  • “有或没有边界检查,取决于我现在的冒险精神”还是从不这样做?那将是愚蠢的
  • 如果你只想要一个值,为什么要使用迭代器?为什么不使用 enumerate 和 fold 计算索引(如果需要,还可以计算值)?
  • C++ 迭代器就像游标; Rust 迭代器更紧密地基于 Python 模型。您可能需要更加努力地考虑需要 max_element 的算法(我发现使用类似 Rust 的迭代器比类似 C++ 的迭代器更容易解决大多数问题,一旦您做出了心理转换)。
  • @trentcl 感觉像是对我的回答 ;-)
  • @Denys 让反对票来吧,但我并没有征求意见,为什么我想在这种简单的情况下这样做。我在问如何,我很高兴得到一个解释或简单的答案你不能在 Rust 中做到这一点。该示例已简化 - 我实际上想附加到最高值,然后从该点循环整个容器(可能使用 cycle() 或类似的东西)。

标签: rust iterator


【解决方案1】:

C++ 迭代器与 Rust 迭代器不同。 Rust 迭代器是只向前的,并且只能被遍历一次。 C++ 迭代器可以被认为是游标。有关详细信息,请参阅What are the main differences between a Rust Iterator and C++ Iterator?

为了以最通用的方式实现您的目标,您必须遍历整个迭代器以找到最大值。在此过程中,每次找到新的最大值时,您都必须复制 迭代器。最后可以返回最大值后点对应的迭代器。

trait MaxElement {
    type Iter;

    fn max_element(self) -> Self::Iter;
}

impl<I> MaxElement for I
where
    I: Iterator + Clone,
    I::Item: PartialOrd,
{
    type Iter = Self;

    fn max_element(mut self) -> Self::Iter {
        let mut max_iter = self.clone();
        let mut max_val = None;

        while let Some(val) = self.next() {
            if max_val.as_ref().map_or(true, |m| &val > m) {
                max_iter = self.clone();
                max_val = Some(val);
            }
        }

        max_iter
    }
}

fn main() {
    let v = vec![1, 3, 2];
    let mut it = v.iter().max_element();
    assert_eq!(Some(&2), it.next());
}

另见:

我实际上想附加到最高值,然后从那个点循环整个容器(可能使用cycle() 或类似的东西)。

在这种情况下,我会尝试更明显:

fn index_of_max(values: &[i32]) -> Option<usize> {
    values
        .iter()
        .enumerate()
        .max_by_key(|(_idx, &val)| val)
        .map(|(idx, _val)| idx)
}

fn main() {
    let v = vec![1, 3, 2];
    let idx = index_of_max(&v).unwrap_or(0);
    let (a, b) = v.split_at(idx);
    let mut it = b.iter().chain(a).skip(1);
    assert_eq!(Some(&2), it.next());
}

另见:

【讨论】:

  • 感谢您的澄清。这实现了我想要实现的目标(在chain() 之后添加了cycle()),但似乎根本没有表现力。此外,我担心这种情况下的性能(与 C++ 相比)——但这需要我自己检查。再次感谢。
【解决方案2】:

如果您想要的只是最大值之后的项目的值,我会通过简单调用 fold 来完成,跟踪目前找到的最大值和相应的下一个值:

fn main() {
    let v = vec![1, 3, 2];
    let nxt = v.iter().fold (
        (None, None),
        |acc, x| {
            match acc {
                (Some (max), _) if x > max => (Some (x), None),
                (Some (max), None) => (Some (max), Some (x)),
                (None, _) => (Some (x), None),
                _ => acc
            }
        }
    ).1;
    assert_eq!(Some(&2), nxt);
}

playground

根据您想对最大值之后的项目做什么,类似的方法可能允许您一次性完成。

【讨论】:

  • 这可能有效。不过,您不同意这个解决方案对于如此微不足道的任务非常复杂吗?
【解决方案3】:

一个简单的解决方案是使用折叠, 以下代码产生“最大数为:99”

    let vv:Vec<i32> = (1..100).collect();
    let largest = vv.iter().fold(std::i32::MIN, |a,b| a.max(*b));
    println!("largest {} ", largest);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-21
    • 2018-01-07
    • 1970-01-01
    • 2021-11-14
    • 2020-02-17
    • 1970-01-01
    • 2021-01-14
    • 1970-01-01
    相关资源
    最近更新 更多