了解收集 `flat_map` 与收集 rust 中的 `map` 之间的细微差别
Posted
技术标签:
【中文标题】了解收集 `flat_map` 与收集 rust 中的 `map` 之间的细微差别【英文标题】:Understanding the nuances between collecting `flat_map` vs collecting `map` in rust 【发布时间】:2022-01-11 18:14:33 【问题描述】:我正在使用 Advent of Code 学习 Rust。 要解析 2021 年第 4 天,我需要解析以下字符串:
88 67 20 19 15
22 76 86 44 73
7 42 6 69 25
12 68 92 21 75
97 45 13 52 70
75 98 24 18 77
17 93 46 49 13
92 56 97 57 66
44 0 65 54 74
23 6 53 42 20
92 94 9 27 41
73 28 62 90 40
78 3 12 37 32
8 86 91 16 30
84 38 68 11 19
理想情况下,我想生成嵌套向量Vec<Vec<&str>>
。
我的问题是我不明白为什么下面的代码不起作用。
let bingo_sheet = grids_str
split("\r\n\r\n")
.map(|grid| grid.split_whitespace())
.collect::<Vec<Vec<&str>>>();
我也不明白货物检查背后的含义。 (好吧,这个特征不存在。但为什么呢?)
error[E0277]: a value of type `Vec<Vec<&str>>` cannot be built from an iterator over elements of type `SplitWhitespace<'_>`
--> src\main.rs:36:10
|
36 | .collect::<Vec<Vec<&str>>>();
| ^^^^^^^ value of type `Vec<Vec<&str>>` cannot be built from `std::iter::Iterator<Item=SplitWhitespace<'_>>`
|
= help: the trait `FromIterator<SplitWhitespace<'_>>` is not implemented for `Vec<Vec<&str>>`
经过一些实验,flat_map
的用法似乎可行。
let bingo_sheet = grids_str
.split("\r\n\r\n")
.flat_map(|grid| grid.split_whitespace())
.collect::<Vec<&str>>();
我不明白这里发生了什么。flat_map
代码的行为符合预期,但 map
代码不符合预期。
【问题讨论】:
.map(|grid| grid.split_whitespace().collect())
可能有效,.map(|grid| grid.split_whitespace().collect::<Vec<&str>>())
应该有效。
这不是“过早的”——你在外部map
中收集内部Vec
,然后收集外部Vec
。你需要(至少)两个collect
s,如果你想收集两个(层)Vec
s。
@gberth 因为FromIterator
是为Vec<T>
实现的,所以你需要先收集它。 flat_map
版本有效,因为您只得到一个 Vec
我建议将每个板表示为一维向量。整体上使用起来更容易,效率也更高。唯一的缺点是,对于索引,您不能使用board[y][x]
,而需要使用board[5 * y + x]
。在我看来,这个缺点真的很小,特别是对于这个问题 - 你真的不需要通过 x
和 y
进行索引。
一个 5x5 板的Vec<Vec<u8>>
有 6 个堆分配和一些外部向量的堆栈存储空间。每个元素访问都需要两个指针间接——首先,跟随堆栈的外部点到包含指向各个行的指针的堆分配,然后跟随指向右行的指针。 [[u8; 5]; 5]
的内存布局在堆栈上是连续的,因此它的指针间接为零并且有可能成为最快的方法之一,但使用起来不是很方便,因为你不能轻易地收集到它。
【参考方案1】:
在我写这篇文章时,cmet 中已经简要回答了这个问题,但也许更详细的版本仍然有帮助!
我也不明白货物检查背后的含义。 (好吧,这个特征不存在。但为什么呢?)
std 提供了经常使用的转换,但它不会为您完成所有转换。
对split_whitespace
的调用会返回一个SplitWhitespace
结构,该结构实现(在其余部分我将简单地说“是”)Iterator
。
对map
的调用返回一个Map
结构,它是对其给定函数的返回类型的迭代器,因此它是对字符串切片的迭代器的迭代器。
因为Map
是一个迭代器,所以它有一个collect
方法。正如您在文档中看到的那样,如果存在实现FromIterator<Self::Item>
的B
,则此方法存在。这个B
是你的Vec<Vec<&str>>
和Self::Item
是Map
的项目SplitWhiteSpace
结构。所以collect
在问,“Vec<Vec<&str>>
是否实现了FromIterator<SplitWhiteSpace>
?如果是,我存在,如果不是,我拒绝存在”。
(FromIterator<SplitWhiteSpace>
并不意味着Vec<T>
可以由SplitWhiteSpace
结构体构成,而是它可以由某种类型I
构成,即(或可以构成)项目@ 的迭代器987654347@.)
让我们查看 Vec 文档,看看 Vec<Vec<&str>>
是否实现了 FromIterator<SplitWhiteSpace>
。 Vec
does implement FromIterator<T>
but only for Vec<T>
,因此在您的情况下,它实现了FromIterator<Vec<&str>>
,但collect 要求它实现FromIterator<SplitWhiteSpace>
。不是这样,您的代码就无法编译。
换句话说,当将Map<X>
收集到Vec<Y>
中时,X 必须等于 Y。
这就是为什么在.map
中添加collect()
有效。因为这使它成为Map<Vec<&str>>
,这意味着类型匹配。 flatmap
也有效,因为这会创建一个 FlatMap<&str>
,其工作方式类似于 Map
,并且可以收集到 Vec<&str>
中。 (flat_map
在其给定函数中不需要 collect
,因为它可以处理其中的迭代器)
【讨论】:
谢谢,@Paul 这很有用。以上是关于了解收集 `flat_map` 与收集 rust 中的 `map` 之间的细微差别的主要内容,如果未能解决你的问题,请参考以下文章