如何在不收集到临时向量的情况下对结果的迭代器执行迭代器计算?

时间:2018-02-17 12:38:16

标签: iterator rust

我正在寻找一种方法来消除此示例中的临时矢量分配:

fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
    println!(
        "{}: {:?}",
        name,
        iter.collect::<Result<Vec<_>, _>>()
            .map(|v| v.into_iter().min())
    );
}

fn main() {
    let without_errors = vec![Ok(1), Ok(2), Ok(3)];
    let with_errors = vec![Ok(1), Err("error"), Ok(2)];

    doit("without errors", without_errors.into_iter());
    doit("with errors", with_errors.into_iter());
}

这是迭代器的一个变体,带有错误处理主题,但我不想创建一个集合(所以collect()并没有做到job),但我想对正在迭代的元素执行进一步的操作。

请注意,这会导致错误的结果,因为Ok小于Err

fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
    println!("{}: {:?}", name, iter.min());
}

它会偶然为max()提供正确的结果,但它不会停止迭代第一个错误。

3 个答案:

答案 0 :(得分:4)

Iterator::try_fold提供了您需要的框架,自Rust 1.27(Playground)以来它可用:

fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
    I: Iterator<Item = Result<T, E>>,
    T: Ord,
    F: Fn(T, T) -> T,
{
    iter.try_fold(None, |r, i| {
        let i = i?;
        Ok(Some(if let Some(r) = r { f(r, i) } else { i }))
    })
}

fn main() {
    let without_errors = vec![Ok(1), Ok(2), Ok(3)];
    let with_errors = vec![Ok(1), Err("error"), Ok(2)];

    fn doit<'r, T>(name: &str, iter: T)
    where
        T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
    {
        println!("{}: {:?}", name, fold_ok(iter.cloned(), ::std::cmp::min));
    }

    doit("without errors", without_errors.iter());
    doit("with errors", with_errors.iter());
}

在此之前,我认为您唯一的选择是手动迭代(Playground

fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
    I: Iterator<Item = Result<T, E>>,
    T: Ord,
    F: Fn(T, T) -> T,
{
    let mut result = match iter.next() {
        None => return Ok(None),
        Some(r) => r?,
    };

    for item in iter {
        result = f(result, item?);
    }

    Ok(Some(result))
}

fn main() {
    let without_errors = vec![Ok(1), Ok(2), Ok(3)];
    let with_errors = vec![Ok(1), Err("error"), Ok(2)];

    fn doit<'r, T>(name: &str, iter: T)
    where
        T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
    {
        println!(
            "{}: {:?}",
            name,
            fold_ok(iter.clone().cloned(), ::std::cmp::min)
        );
    }

    doit("without errors", without_errors.iter());
    doit("with errors", with_errors.iter());
}

答案 1 :(得分:4)

“提升”一个处理结果迭代器的函数是一种相当常见的模式,像往常一样,itertools有一个解决方案 - process_results

use itertools; // 0.8.0

fn doit(name: &str, iter: impl Iterator<Item = Result<i32, &'static str>>) {
    let min = itertools::process_results(iter, |i| i.min());
    println!("{}: {:?}", name, min);
}

此代码在被提取到itertools之前以ResultShunt in the standard library开头。对于sum的迭代器,productResult的实现是其基础。

答案 2 :(得分:0)

可能会滥用ffmpeg -c:v libvpx -i in.webm -c:v libvpx -vf scale=100:100 -auto-alt-ref 0 out.webm

collect()

这可以通过pub struct Min<T> { value: Option<T>, } impl<T> Min<T> { pub fn value(self) -> Option<T> { self.value } } impl<T> std::iter::FromIterator<T> for Min<T> where T: Ord, { fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { let mut iter = iter.into_iter(); match iter.next() { None => Min { value: None }, Some(mut value) => { for i in iter { value = std::cmp::min(value, i); } Min { value: Some(value) } } } } } 使用。这是很多机器,我没有看到一种方法来抽象它(所以你只需要提供iter.collect::<Min<_>>().value()或其他一些半群操作。)

我没有朝Iterator::try_fold的方向看,它提供了大部分机器。