我需要帮助重构 Rust 中的错误处理
I need help refactoring for error handling in Rust
我想重构这个用于计算最大级数乘积的 Rust 代码,并使其尽可能高效和优雅。我觉得
lsp(string_digits: &str, span: usize) -> Result<u64, Error>
可以通过某种方式使它比现在更优雅。 lsp 可以只用一系列链式迭代器方法实现吗?
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: &[u8]) -> u64 {
w.iter().fold(1u64, |acc, &d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
let invalid_chars = string_digits
.chars()
.filter(|ch| !ch.is_numeric())
.collect::<Vec<_>>();
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if !invalid_chars.is_empty() {
return Err(Error::InvalidDigit(invalid_chars[0]));
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let vec_of_u8_digits = string_digits
.chars()
.map(|ch| ch.to_digit(10).unwrap() as u8)
.collect::<Vec<_>>();
let lsp = vec_of_u8_digits
.windows(span)
.max_by(|&w1, &w2| sp(w1).cmp(&sp(w2)))
.unwrap();
Ok(sp(lsp))
}
不确定这是否是最优雅的方式,但我试过了,希望新版本与给定程序等效。
在这种情况下需要两件事:首先,我们需要一个提供滑动 window "on the fly" 的数据结构,其次,如果转换产生错误,我们需要一个提前结束迭代的函数.
对于前者,我选择了 VecDeque,因为 span
是动态的。对于后者,在 itertools crate 中有一个名为 process_results
的函数。它将结果上的迭代器转换为展开类型上的迭代器,并在遇到错误时停止迭代。
我还稍微更改了 sp
的签名以接受 u8 上的任何迭代器。
这是代码:
use std::collections::VecDeque;
use itertools::process_results;
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: impl Iterator<Item=u8>) -> u64 {
w.fold(1u64, |acc, d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let mut init_state = VecDeque::new();
init_state.resize(span, 0);
process_results(string_digits.chars()
.map(|ch| ch.to_digit(10)
.map(|d| d as u8)
.ok_or(Error::InvalidDigit(ch))),
|digits|
digits.scan(init_state, |state, digit| {
state.pop_back();
state.push_front(digit);
Some(sp(state.iter().cloned()))
})
.max()
.unwrap()
)
}
我想重构这个用于计算最大级数乘积的 Rust 代码,并使其尽可能高效和优雅。我觉得
lsp(string_digits: &str, span: usize) -> Result<u64, Error>
可以通过某种方式使它比现在更优雅。 lsp 可以只用一系列链式迭代器方法实现吗?
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: &[u8]) -> u64 {
w.iter().fold(1u64, |acc, &d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
let invalid_chars = string_digits
.chars()
.filter(|ch| !ch.is_numeric())
.collect::<Vec<_>>();
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if !invalid_chars.is_empty() {
return Err(Error::InvalidDigit(invalid_chars[0]));
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let vec_of_u8_digits = string_digits
.chars()
.map(|ch| ch.to_digit(10).unwrap() as u8)
.collect::<Vec<_>>();
let lsp = vec_of_u8_digits
.windows(span)
.max_by(|&w1, &w2| sp(w1).cmp(&sp(w2)))
.unwrap();
Ok(sp(lsp))
}
不确定这是否是最优雅的方式,但我试过了,希望新版本与给定程序等效。
在这种情况下需要两件事:首先,我们需要一个提供滑动 window "on the fly" 的数据结构,其次,如果转换产生错误,我们需要一个提前结束迭代的函数.
对于前者,我选择了 VecDeque,因为 span
是动态的。对于后者,在 itertools crate 中有一个名为 process_results
的函数。它将结果上的迭代器转换为展开类型上的迭代器,并在遇到错误时停止迭代。
我还稍微更改了 sp
的签名以接受 u8 上的任何迭代器。
这是代码:
use std::collections::VecDeque;
use itertools::process_results;
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: impl Iterator<Item=u8>) -> u64 {
w.fold(1u64, |acc, d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let mut init_state = VecDeque::new();
init_state.resize(span, 0);
process_results(string_digits.chars()
.map(|ch| ch.to_digit(10)
.map(|d| d as u8)
.ok_or(Error::InvalidDigit(ch))),
|digits|
digits.scan(init_state, |state, digit| {
state.pop_back();
state.push_front(digit);
Some(sp(state.iter().cloned()))
})
.max()
.unwrap()
)
}