如何在Rust中获取最大值元素的迭代器?

7
我想访问在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()或类似的东西)。

根据我当时的冒险心情,可以选择进行边界检查或不进行边界检查。但是如果这样做的话会很愚蠢。 - Stargateur
如果你只想要一个值,为什么要使用迭代器?为什么不使用enumerate和fold计算索引(以及值,如果需要)呢? - Denys Séguret
2
C++ 迭代器就像光标;Rust 迭代器更接近于 Python 模型。你可能需要更深入地思考需要 max_element 的算法(我发现,一旦你进行了心理转换,大多数问题使用 Rust 类似的迭代器比 C++ 类似的迭代器更容易解决)。 - trent
@trentcl 对我来说就像是一个答案;-) - Shepmaster
1
@Denys 让踩票来吧,但我不是在征求意见,为什么我想以这种方式处理这么简单的情况。我在问如何,如果有解释或简单的你不能在Rust中这样做的答案,我会很高兴。这个例子是简化的 - 实际上我想要连接到最高值,然后从那个点开始循环整个容器(可能使用cycle()或类似的东西)。 - Leśny Rumcajs
3个回答

8
一个简单的解决方案是使用fold,下面的代码可以生成"largest num is: 99"。
    let vv:Vec<i32> = (1..100).collect();
    let largest = vv.iter().fold(std::i32::MIN, |a,b| a.max(*b));
    println!("largest {} ", largest);

7

C++的迭代器和Rust的迭代器并不相同。 Rust的迭代器是单向的,只能被遍历一次。 C++的迭代器可以被视为光标。更多细节请参见什么是 Rust 迭代器和 C++ 迭代器之间的主要区别?

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

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++相比)-但这需要我自己检查。再次感谢。 - Leśny Rumcajs
另一种遍历所有元素的选项是使用BinaryHeap并从中弹出N个元素。 - Schneems

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);
}

游乐场

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


2
这可能会起作用。但是,您不认为对于如此微不足道的任务来说,这个解决方案过于复杂了吗? - Leśny Rumcajs
1
这只是一个简单的"调用折叠"而已,拜托!你怎么能说这个复杂呢?!!! - user3773048

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接