连接一个字符串向量的向量

4
我正在尝试编写一个函数,它接收一个字符串向量的向量并返回所有连接在一起的向量,即它返回一个字符串向量。
到目前为止,我能做到最好的是以下内容:
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<String>,但是我无法弄清楚如何做到这一点。
我更加想知道为什么collect的返回类型确切地是Vec<&String>。我尝试从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=&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<String>...

请帮我澄清这个混乱。

编辑:得益于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<String>,而以前它总是给我Vec<&String>。虽然看到高级概念如何反映在库的工作方式上确实非常酷,但我希望我知道这是如何实现的。
编辑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<T = Vec<String>,Item = Vec<String>>:这是不正确的。结构体Iter上没有名为Item的关联类型(只有特征可以有关联类型)。vecs.iter()的类型为Iter<Vec<String>>,但是此类型实现了Iterator<Item=&Vec<String>>(请注意&)。当您使用flat_map时,将&Vec<String>转换为&String
- Francis Gagné
1个回答

6
我会考虑:为什么在外部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() /* -> Vec<String> */
使用.flat_map的方法是可以的,但如果您不想再次克隆字符串,则必须在所有级别上使用.into_iter():(xVec<String>)。 vecs.into_iter().flat_map(|x| x.into_iter()).collect() 如果您想克隆每个字符串,可以使用以下方法:(将.into_iter()更改为.iter(),因为此处的x&Vec<String>,而这两种方法实际上得到的结果相同!) 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 - kirillkh
可能是因为我在提交之前没有进行测试编译,但后来我修复了它。 - bluss
我该如何独立学习vecs.concat() ?它不在std::vec :: Vec的API页面上。我的意思是,如果查看API没有任何提示,我应该如何找到它?即使现在我实证知道它存在,我也无法弄清楚它的定义在哪里。 - kirillkh
好的,这回答了我的主要问题,我真的很想将您的答案标记为解决方案,但另一方面,我也想得到关于元问题(如何通过查看API来确定类型)的答案。也许我的问题太广泛了? - kirillkh
谢谢Veedrac。如果你不知道concat在哪里,你会怎么找它?我甚至试着在Google上搜索它,但没有成功。 - kirillkh
显示剩余4条评论

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