我有一个迭代器类型对象,每次调用它都可以返回零个,一个或多个项目。我想实现标准Iter
API,即next
返回Option<Self::Item>
,因此可以逐项消费。
在Clojure中,我可能会使用mapcat
(&#34; map and concatenate&#34;)。
我目前的解决方案(感谢@Ryan)使用flat_map
但仍需要大量分配:
// Desired input:
// A stateful object that implements an iterator which returns a number of results each time.
// The real code is a bit more complicated, this is the minimal example.
struct MyThing {
counter: i32,
}
impl Iterator for MyThing {
type Item = Vec<String>;
fn next(&mut self) -> Option<Vec<String>> {
self.counter += 1;
if self.counter == 4 {
self.counter = 1;
}
match self.counter {
1 => Some(vec!["One".to_string()]),
2 => Some(vec!["One".to_string(), "Two".to_string()]),
3 => Some(vec![
"One".to_string(),
"Two".to_string(),
"Three".to_string(),
]),
_ => Some(vec![]),
}
}
}
fn main() {
let things = MyThing { counter: 0 };
// Missing piece, though the following line does the job:
let flattened = things.flat_map(|x| x);
// However this requires a heap allocation at each loop.
// Desired output: I can iterate, item by item.
for item in flattened {
println!("{:?}", item);
}
}
鉴于我所看到的创新事物,我想知道是否有更惯用,更便宜的方式来实现这种模式。
答案 0 :(得分:1)
如果您知道如何以编程方式生成“内部”值,请将Vec<String>
替换为您定义的实现Iterator<Item = String>
的结构。 (技术上只需要IntoIterator
,但Iterator
就足够了。)
struct Inner {
index: usize,
stop: usize,
}
impl Inner {
fn new(n: usize) -> Self {
Inner { index: 0, stop: n }
}
}
impl Iterator for Inner {
type Item = String;
fn next(&mut self) -> Option<String> {
static WORDS: [&str; 3] = ["One", "Two", "Three"];
let result = if self.index < self.stop {
WORDS.get(self.index).map(|r| r.to_string())
} else {
None
};
self.index += 1;
result
}
}
由于Inner
实现了Iterator<Item = String>
,因此可以像Vec<String>
那样进行迭代。但是Inner
不必预先分配Vec
并逐个使用项目;它可以根据需要懒洋洋地创建每个String
。
“外部”迭代器只是一个实现Iterator<Item = Inner>
的结构,同样懒惰地构造每个Inner
:
struct Outer {
counter: i32,
}
impl Iterator for Outer {
type Item = Inner;
fn next(&mut self) -> Option<Inner> {
self.counter = 1 + self.counter % 3;
Some(Inner::new(self.counter as usize))
}
}
如您所知,Iterator::flat_map
会使嵌套结构变平,所以类似于以下内容works:
let things = Outer { counter: 0 };
for item in things.flat_map(|x| x).take(100) {
println!("{:?}", item);
}
在现实代码中,Inner
和Outer
在大多数情况下可能与此示例有很大不同。例如,如果不等同于分配Inner
,则不一定可以编写Vec
。因此,这些迭代器的精确形状和语义取决于有关用例的具体信息。
以上假设Inner
在某种程度上有用,或者更容易实现。您可以轻松编写一个迭代序列而不需要展平的结构,但您还必须将内部迭代器状态(index
字段)放入Outer
:
struct Outer {
index: usize,
counter: i32,
}
impl Iterator for Outer {
type Item = String;
fn next(&mut self) -> Option<String> {
static WORDS: [&str; 3] = ["One", "Two", "Three"];
let result = WORDS.get(self.index).map(|r| r.to_string());
self.index += 1;
if self.index >= self.counter as usize {
self.counter = 1 + self.counter % 3;
self.index = 0;
};
result
}
}
fn main() {
let things = Outer { counter: 1, index: 0 };
for item in things.take(100) {
println!("{:?}", item);
}
}