Мне нужна помощь рефакторинга для обработки ошибок в Rust - PullRequest
0 голосов
/ 09 марта 2019

Я хотел бы реорганизовать этот код 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))
}

1 Ответ

1 голос
/ 09 марта 2019

Не уверен, что это самый элегантный способ, но я попробовал, надеюсь, новая версия эквивалентна данной программе.

В этом случае понадобятся две вещи: во-первых,нам нужна структура данных, которая обеспечивает скользящее окно «на лету», а вторая функция, которая завершает итерацию раньше, если преобразование выдает ошибку.

Для первого я выбрал 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()
    )
}
...