【问题标题】:Collect items from an iterator at a specific index从特定索引处的迭代器中收集项目
【发布时间】:2015-08-13 11:15:07
【问题描述】:

我想知道是否可以在迭代器上使用.collect() 来获取特定索引处的项目。例如,如果我以字符串开头,我通常会这样做:

let line = "Some line of text for example";
let l = line.split(" ");
let lvec: Vec<&str> = l.collect();
let text = &lvec[3];

但更好的是:

let text: &str = l.collect(index=(3));

【问题讨论】:

    标签: rust


    【解决方案1】:

    不,不是;但是,您可以在收集之前轻松过滤,这实际上可以达到相同的效果。

    如果你想按索引过滤,你需要添加索引,然后再剥离它:

    • enumerate(为元素添加索引)
    • filter 基于此索引
    • map 从元素中去除索引

    或者在代码中:

    fn main() {
        let line = "Some line of text for example";
        let l = line.split(" ")
                    .enumerate()
                    .filter(|&(i, _)| i == 3 )
                    .map(|(_, e)| e);
        let lvec: Vec<&str> = l.collect();
        let text = &lvec[0];
        println!("{}", text);
    }
    

    如果您只希望获得单个索引(以及元素),那么使用nth 会容易得多。它在这里返回一个Option&lt;&amp;str&gt;,您需要注意:

    fn main() {
        let line = "Some line of text for example";
        let text = line.split(" ").nth(3).unwrap();
        println!("{}", text);
    }
    

    如果你可以有一个任意谓词但只希望第一个匹配的元素,那么收集到Vec 是低效的:它将消耗整个迭代器(没有惰性)并可能分配大量不需要的内存完全没有。

    因此,您最好使用迭代器的next 方法简单地请求第一个元素,该方法在此处返回Option&lt;&amp;str&gt;

    fn main() {
        let line = "Some line of text for example";
        let text = line.split(" ")
                       .enumerate()
                       .filter(|&(i, _)| i % 7 == 3 )
                       .map(|(_, e)| e)
                       .next()
                       .unwrap();
        println!("{}", text);
    }
    

    如果你想选择部分的结果,按索引,你也可以在收集之前使用skiptake,但我想你已经有足够的选择了。 p>

    【讨论】:

    • 谢谢,这里有很多东西可以吸收,但非常有用。您提到收集到 Vec 效率低下。您能否评论执行 line.split.enumerate.filter.map.unwrap 多次以从字符串中获取不同项目与将整个事物收集到 Vec 然后使用矢量索引来检索项目的速度。例如,根据经验,如果需要访问 1/4​​ 的项目,我应该采用哪种方法?
    • @kezzos:我提到将 everything 收集到向量中是低效的,我也给出了原因:当你不关心最后一个时,迭代到最后件和分配内存。如果您需要收集 1/4 的片段,那么我不会担心内存分配,但是您仍然可以从缩短迭代中获益(使用 take)。
    • 还有filter_mapfiltermap 步骤结合起来。
    【解决方案2】:

    Iterator 上有一个 nth 函数可以执行此操作:

    let text = line.split(" ").nth(3).unwrap();
    

    【讨论】:

    • 谢谢,这是否也返回一个选项?
    • 当然,这就是unwrap 的用途。基本上是fn unwrap(x : Option&lt;T&gt;) { if let Some(v) = x { v } else { panic!("Meh") } }
    【解决方案3】:

    没有;你可以使用takenext,不过:

    let line = "Some line of text for example";
    let l = line.split(" ");
    let text = l.skip(3).next();
    

    请注意,这会导致text 成为Option&lt;&amp;str&gt;,因为不能保证序列实际上具有至少四个元素。

    附录:使用nth 肯定更短,但我更愿意明确说明访问 nth 元素的事实迭代器必然消耗它之前的所有元素。

    【讨论】:

    • 关于你的附录,我明白你的意思。然而,rust 无论如何都是非常明确的:如果你把它写在一行中,无论如何都很好,因为你不会在其他任何地方使用(部分消耗的)拆分。如果你想拆分拆分得到第4项,编译器会强制你写let mut l = line.split(" ")才能使用nth
    【解决方案4】:

    对于任何可能感兴趣的人,您可以使用迭代器做很多很酷的事情(感谢 Matthieu M),例如根据索引从字符串中获取多个“单词”,您可以使用 filter 和逻辑或|| 来测试多个索引!

    let line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062"
    let words: Vec<&str> = line.split(" ")
                               .enumerate()
                               .filter(|&(i, _)| i==1 || i==3 || i==6 )
                               .map(|(_, e) | e)
                               .collect();
    

    【讨论】:

      猜你喜欢
      • 2018-12-04
      • 2021-04-20
      • 2014-01-15
      • 1970-01-01
      • 1970-01-01
      • 2015-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多