【问题标题】:Concatenate a vector of vectors of strings连接字符串向量的向量
【发布时间】:2015-06-29 21:40:01
【问题描述】:

我正在尝试编写一个函数,它接收字符串向量的向量并返回连接在一起的所有向量,即它返回一个字符串向量。

到目前为止,我能做的最好的事情如下:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let vals : Vec<&String> = vecs.iter().flat_map(|x| x.into_iter()).collect();
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

但是,我对这个结果并不满意,因为似乎我应该能够从第一个 collect 调用中获得 Vec&lt;String&gt;,但不知何故我无法弄清楚如何去做。

我更感兴趣的是为什么collect 的返回类型是Vec&lt;&amp;String&gt;。我试图从 API 文档和源代码中推断出这一点,但尽管我尽了最大努力,但我什至无法理解函数的签名。

所以让我试着追踪每个表达式的类型:

- vecs.iter(): Iter<T=Vec<String>, Item=Vec<String>>
- vecs.iter().flat_map(): FlatMap<I=Iter<Vec<String>>, U=???, F=FnMut(Vec<String>) -> U, Item=U>
- vecs.iter().flat_map().collect(): (B=??? : FromIterator<U>)
- vals was declared as Vec<&String>, therefore 
      vals == vecs.iter().flat_map().collect(): (B=Vec<&String> : FromIterator<U>). Therefore U=&String.

我在上面假设类型推断器能够根据vals 的类型找出U=&amp;String。但是,如果我在代码中为表达式提供显式类型,则编译不会出错:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let a: Iter<Vec<String>> = vecs.iter();
    let b: FlatMap<Iter<Vec<String>>, Iter<String>, _> = a.flat_map(|x| x.into_iter());
    let c = b.collect();
    print_type_of(&c);
    let vals : Vec<&String> = c;
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

很明显,U=Iter&lt;String&gt;...请帮我收拾一下这个烂摊子。

编辑:感谢 bluss 的提示,我能够实现一个 collect,如下所示:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    vecs.into_iter().flat_map(|x| x.into_iter()).collect()
}

我的理解是,通过使用into_iter,我将vecs 的所有权转移到IntoIter 并进一步向下调用链,这使我可以避免复制 lambda 调用中的数据,因此 - 神奇地 - 类型系统给我Vec&lt;String&gt;,以前它总是给我Vec&lt;&amp;String&gt;。虽然看到高级概念如何反映在库的工作中当然非常酷,但我希望我知道这是如何实现的。

编辑 2:经过费力的猜测,查看 API 文档并使用 this method 破译类型,我得到了完整的注释(不考虑生命周期):

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let a: Iter<Vec<String>> = vecs.iter();
    let f : &Fn(&Vec<String>) -> Iter<String> = &|x: &Vec<String>| x.into_iter();
    let b: FlatMap<Iter<Vec<String>>, Iter<String>, &Fn(&Vec<String>) -> Iter<String>> = a.flat_map(f);
    let vals : Vec<&String> = b.collect();
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

【问题讨论】:

  • - vecs.iter(): Iter&lt;T=Vec&lt;String&gt;, Item=Vec&lt;String&gt;&gt;:这是不正确的。 struct Iter 上没有名为 Item 的关联类型(只有特征可能具有关联类型)。 vecs.iter()Iter&lt;Vec&lt;String&gt;&gt; 类型,但 this type implements Iterator&lt;Item=&amp;Vec&lt;String&gt;&gt;(注意 &amp;)。当你 flat_map 这个时,你把 &amp;Vec&lt;String&gt; 变成 &amp;String

标签: vector concatenation rust


【解决方案1】:

我想:为什么你在外部 vec 上使用 iter() 而在内部 vec 上使用 into_iter() ?使用into_iter() 实际上是至关重要的,这样我们就不必先复制内部向量,然后复制其中的字符串,我们只需获得它们的所有权。

我们实际上可以把它写成求和:两个两个连接向量。由于我们总是重复使用同一个累加向量的分配和内容,所以这个操作是线性时间。

为了最大限度地减少增长和重新分配向量所花费的时间,请预先计算所需的空间。

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let size = vecs.iter().fold(0, |a, b| a + b.len());
    vecs.into_iter().fold(Vec::with_capacity(size), |mut acc, v| {
        acc.extend(v); acc
    })
}

如果你确实想克隆所有内容,已经有一个方法,你只需使用vecs.concat() /* -&gt; Vec&lt;String&gt; */


.flat_map 的方法很好,但如果您不想再次克隆字符串,则必须在所有级别上使用 .into_iter():(xVec&lt;String&gt;)。

vecs.into_iter().flat_map(|x| x.into_iter()).collect()

如果你想克隆每个字符串,你可以使用这个:(将.into_iter()更改为.iter(),因为x这里是&amp;Vec&lt;String&gt;,这两种方法实际上会产生相同的结果!)

vecs.iter().flat_map(|x| x.iter().map(Clone::clone)).collect()

【讨论】:

  • 这给了我一些错误:` build.rs:104:9: 104:12 错误:不能借用不可变局部变量 acc 作为可变 build.rs:104 acc.extend(v); v ^~~ 注意:在扩展闭包扩展 build.rs:103:53:105:6 注意:扩展站点 build.rs:104:24:104:25 错误:使用移动值:v [E0382] build.rs:104 acc.extend(v); v`
  • 可能是因为我在提交之前没有测试编译它,但后来我修复了它
  • 如何独立学习 vecs.concat()?它不在 std::vec::Vec 的 API 页面上。我的意思是,如果查看 API 没有给出任何提示,我应该如何找到它?即使现在我凭经验知道它在那里,我也无法弄清楚它是在哪里定义的。
  • 关于您使用 fold() 的解决方案,谢谢。尽管如此,我所追求的是学习如何独立实现所需的结果,所以我真的很想以我的代码为起点,看看如何改进它以仅使用一个 collect() 而不是重写。跨度>
  • 嗯,这回答了我的主要问题,我真的很想将你的答案标记为解决方案,但是我也想得到元问题的答案(如何通过查看 API)。也许我的问题太宽泛了?
猜你喜欢
  • 2011-01-07
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
  • 2013-12-21
相关资源
最近更新 更多