编者注:这个代码示例来自1.0之前的Rust版本,当时实现了许多迭代器
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;
}
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用,永远循环.似乎它iter每次调用都使用一个克隆take_while,从同一个位置一遍又一遍地开始.我希望iter每次都使用相同的方法,在所有each_times上推进相同的迭代器.这可能吗?
huo*_*uon 14
正如您所指出的,每个take_while调用都是重复的iter,因为take_whiletake self和Peekablechars迭代器是Copy.(仅在Rust 1.0之前为真 - 编辑)
您希望每次都修改迭代器,也就是说,要take_while对&mut迭代器进行操作.这正是.by_ref适配器的用途:
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"))
}
Run Code Online (Sandbox Code Playgroud)
打印
["123", "bc", "56", "ef"]
Run Code Online (Sandbox Code Playgroud)
但是,我想这不正确.
我实际上建议for使用char_indices迭代器将其写为普通循环:
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
}
Run Code Online (Sandbox Code Playgroud)
这种形式也允许用更少的分配(也就是说,String不需要s)来做到这一点,因为每个返回的值只是一个切片input,我们可以使用生命周期来说明:
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
}
Run Code Online (Sandbox Code Playgroud)
所有改变的是类型签名,删除Vec<String>类型提示和.to_string调用.
甚至可以编写这样的迭代器,以避免分配Vec.类似于fn split<'a>(input: &'a str) -> Splits<'a> { /* construct a Splits */ }哪里的东西Splits是一个实现的结构Iterator<&'a str>.
take_while按值获取self:它消耗迭代器。不幸的是,在 Rust 1.0 之前,它也能够被隐式复制,从而导致您所观察到的令人惊讶的行为。
由于这些原因,您无法用于take_while您想要的用途。您将需要手动展开您的take_while调用。
以下是处理此问题的多种可能方法之一:
\n\npub fn split(input: &str) -> Vec<String> {\n let mut bits: Vec<String> = vec![];\n let mut iter = input.chars().peekable();\n loop {\n let seeking_digits = match iter.peek() {\n None => return bits,\n Some(c) => c.is_digit(10),\n };\n if seeking_digits {\n bits.push(take_while(&mut iter, |c| c.is_digit(10)));\n } else {\n bits.push(take_while(&mut iter, |c| !c.is_digit(10)));\n }\n }\n}\n\nfn take_while<I, F>(iter: &mut std::iter::Peekable<I>, predicate: F) -> String\nwhere\n I: Iterator<Item = char>,\n F: Fn(&char) -> bool,\n{\n let mut out = String::new();\n loop {\n match iter.peek() {\n Some(c) if predicate(c) => out.push(*c),\n _ => return out,\n }\n let _ = iter.next();\n }\n}\n\nfn main() {\n println!("{:?}", split("test123test"));\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这产生了具有两级循环的解决方案;另一种有效的方法是将其建模为仅一层深的状态机。询问您是否\xe2\x80\x99 不确定我的意思,我\xe2\x80\x99 将演示。
\n| 归档时间: |
|
| 查看次数: |
6191 次 |
| 最近记录: |