在Rust中,重复使用迭代器的最有效方法是什么?

19

我想重新使用我创建的迭代器,以避免需要从头开始再次创建它。但是迭代器似乎无法进行clone操作,而collect会移动迭代器,因此我无法重复使用它。

以下大概是我尝试做的等价事情。

let my_iter = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() );
let my_struct = {
  one: my_iter.collect(),
  two: my_iter.map(|c|{(c,Vec::new())}).collect(),
  three: my_iter.filter_map(|c|if c.predicate(){Some(c)}else{None}).collect(),
  four: my_iter.map(|c|{(c,1.0/my_float)}).collect(),
  five: my_iter.map(|c|(c,arg_time.unwrap_or(time::now()))).collect(),
  //etc...
}
3个回答

17

在优化任何东西之前,您应该进行分析,否则您可能会使事情变得比所需的更加缓慢和复杂。

您示例中的迭代器

let my_iter = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() );

栈上分配的是一种薄结构。克隆它们并不比从头开始构建便宜多少。

使用 .chars().flat_map(|c| c.to_uppercase()) 构造一个迭代器只需要1纳秒,根据基准测试结果。

根据同样的基准测试,将迭代器创建包装在一个闭包中所需的时间比直接原地构建迭代器更长。

克隆 Vec 迭代器的速度与原地构建几乎相同,两者都几乎瞬间完成。

test construction_only    ... bench:           1 ns/iter (+/- 0)
test inplace_construction ... bench:         249 ns/iter (+/- 20)
test closure              ... bench:         282 ns/iter (+/- 18)
test vec_inplace_iter     ... bench:           0 ns/iter (+/- 0)
test vec_clone_iter       ... bench:           0 ns/iter (+/- 0)

14

一般情况下,如果迭代器的所有“部分”都是可克隆的,那么它们通常是可克隆的。在my_iter中有几个不可克隆的部分:匿名闭包(例如flat_map中的一个)和由to_uppercase返回的ToUppercase结构体。

你可以这样做:

  1. 重建整个事物(如@ArtemGr所建议的)。你可以使用宏来避免重复。有点丑陋但应该有效。
  2. 在填充my_struct之前将my_iter收集到Vec中(因为你似乎已经在其中收集了它):let my_iter: Vec<char> = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() ).collect();
  3. 创建自己的定制迭代器。没有my_stringTag的定义(因为你在其上调用了unwrap_or,我假设它不是String),所以很难给你更具体的帮助。

2
几年后,所有这些部件都已经可克隆:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=57f68b06d81e4f798e90c0bb8d38beea - Jason Orendorff

5
您可以使用闭包来获取相同的迭代器:
#[derive(Debug)]
struct MyStruct{
    one:Vec<char>,
    two:Vec<char>,
    three:String
}

fn main() {
    let my_string:String = "ABCD1234absd".into();
    let my_iter = || my_string.chars();
    let my_struct = MyStruct{
        one: my_iter().collect(),
        two: my_iter().filter(|x| x.is_numeric()).collect(),
        three: my_iter().filter(|x| x.is_lowercase()).collect()
    };
    println!("{:?}", my_struct);
}

参见这个正确返回迭代器的方法?问题。

此外,您可以克隆迭代器(请参见有关迭代器可克隆性的@Paolo Falabella答案):

fn main() {
    let v = vec![1,2,3,4,5,6,7,8,9]; 
    let mut i = v.iter().skip(2);
    let mut j = i.clone();
    println!("{:?}", i.take(3).collect::<Vec<_>>());
    println!("{:?}", j.filter(|&x| x%2==0).collect::<Vec<_>>());
}

抱歉,我无法确定哪种方法更有效。

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