Не уверен, что это самый элегантный способ, но я попробовал, надеюсь, новая версия эквивалентна данной программе.
В этом случае понадобятся две вещи: во-первых,нам нужна структура данных, которая обеспечивает скользящее окно «на лету», а вторая функция, которая завершает итерацию раньше, если преобразование выдает ошибку.
Для первого я выбрал VecDeque, так как span
динамичноДля последнего есть функция с именем process_results
в корзине itertools.Он преобразует итератор по результатам в итератор по развернутому типу и останавливает итерацию, если обнаружена ошибка.
Я также слегка изменил сигнатуру 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()
)
}