在Rust中多次使用同一迭代器
编者按:此代码示例来自Rust 1.0之前的一个版本,当时许多迭代器实现了在Rust中多次使用同一迭代器,rust,Rust,编者按:此代码示例来自Rust 1.0之前的一个版本,当时许多迭代器实现了Copy。此代码的更新版本会产生不同的错误,但答案仍然包含有价值的信息 我正在尝试编写一个函数,将字符串拆分为一堆字母和数字;例如,“test123test”将变成[“test”、“123”、“test”]。以下是我迄今为止的尝试: pub fn split(input: &str) -> Vec<String> { let mut bits: Vec<String> = ve
Copy
。此代码的更新版本会产生不同的错误,但答案仍然包含有价值的信息
我正在尝试编写一个函数,将字符串拆分为一堆字母和数字;例如,“test123test”
将变成[“test”、“123”、“test”]
。以下是我迄今为止的尝试:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
match iter.peek() {
None => return bits,
Some(c) => if c.is_digit() {
bits.push(iter.take_while(|c| c.is_digit()).collect());
} else {
bits.push(iter.take_while(|c| !c.is_digit()).collect());
}
}
}
return bits;
}
pub-fn-split(输入:&str)->Vec{
设mut位:Vec=Vec![];
让mut iter=input.chars().peek();
环路{
匹配iter.peek(){
无=>返回位,
一些(c)=>如果c.是数字(){
bits.push(iter.take_while(|c | c.is_digit()).collect());
}否则{
bits.push(iter.take_while(|c |!c.is_digit()).collect());
}
}
}
返回位;
}
然而,这不起作用,永远循环。似乎每次我调用
take_while
,它都在使用一个iter
的克隆,从同一个位置一次又一次地开始。我希望它每次都使用相同的iter
,在所有每次
s上推进相同的迭代器。这可能吗?take\u而按值获取self
:它消耗迭代器。不幸的是,在Rust 1.0之前,它还能够被隐式复制,从而导致您观察到的令人惊讶的行为
由于这些原因,您不能使用take_while
来满足您的需求。您需要在调用时手动展开take\u
以下是处理此问题的多种可能方法之一:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
let seeking_digits = match iter.peek() {
None => return bits,
Some(c) => c.is_digit(10),
};
if seeking_digits {
bits.push(take_while(&mut iter, |c| c.is_digit(10)));
} else {
bits.push(take_while(&mut iter, |c| !c.is_digit(10)));
}
}
}
fn take_while<I, F>(iter: &mut std::iter::Peekable<I>, predicate: F) -> String
where
I: Iterator<Item = char>,
F: Fn(&char) -> bool,
{
let mut out = String::new();
loop {
match iter.peek() {
Some(c) if predicate(c) => out.push(*c),
_ => return out,
}
let _ = iter.next();
}
}
fn main() {
println!("{:?}", split("test123test"));
}
pub-fn-split(输入:&str)->Vec{
设mut位:Vec=Vec![];
让mut iter=input.chars().peek();
环路{
让搜索数字=匹配iter.peek(){
无=>返回位,
有些(c)=>c是数字(10),
};
如果正在查找\u个数字{
位推(take_while(&mut iter,| c | c.is_digital(10));
}否则{
位推送(取时(&mut iter,| c |!c.is_位(10));
}
}
}
fn take_while(iter:&mut std::iter::Peekable,谓词:F)->String
哪里
I:迭代器,
F:Fn(&char)->bool,
{
let mut out=String::new();
环路{
匹配iter.peek(){
一些(c)if谓词(c)=>out.push(*c),
_=>返回,
}
让u=iter.next();
}
}
fn main(){
println!(“{:?}”,拆分(“test123test”);
}
这将产生一个具有两级循环的解决方案;另一种有效的方法是将其建模为一个仅在一个层次上的状态机。如果你不确定我的意思,我将演示。正如你所指出的,每个take_while
调用都是重复的iter
,因为take_while
takeself
和可查看的字符迭代器是。(仅在Rust 1.0之前为真-编辑器)
您希望每次都修改迭代器,也就是说,take_while
在迭代器的&mut
上操作。这正是适配器的用途:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
match iter.peek().map(|c| *c) {
None => return bits,
Some(c) => if c.is_digit(10) {
bits.push(iter.by_ref().take_while(|c| c.is_digit(10)).collect());
} else {
bits.push(iter.by_ref().take_while(|c| !c.is_digit(10)).collect());
},
}
}
}
fn main() {
println!("{:?}", split("123abc456def"))
}
然而,我认为这是不正确的
实际上,我建议使用迭代器将其作为正常的for
循环编写:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(input[start..i].to_string());
is_digit = this_is_digit;
start = i;
}
}
bits.push(input[start..].to_string());
bits
}
更改的只是类型签名,删除了Vec
类型提示和.to_字符串
调用
人们甚至可以编写这样的迭代器,以避免分配Vec
。类似于fn split Splits
的东西,从2015年2月起,可查看的不是副本
,因此原始代码不会无限循环——它会抛出一个编译错误,指示在调用take\u
时发生所有权更改。例如,请参见:itertools
板条箱提供了一种peek\u take\u while
方法,该方法基本上实现了该答案函数的功能,但采用了通用方式。
pub fn split<'a>(input: &'a str) -> Vec<&'a str> {
let mut bits = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(&input[start..i]);
is_digit = this_is_digit;
start = i;
}
}
bits.push(&input[start..]);
bits
}