functional-programming - 有没有更好的功能方法来处理带有错误检查的向量?

标签 functional-programming rust

我正在学习 Rust,想知道如何改进下面的代码。

我有一个形式为 (u32, String) 的元组向量. u32值表示行号和 String s 是相应行上的文本。只要所有 String 值都可以成功解析为整数,我想返回一个 Ok<Vec<i32>>包含刚刚解析的 String值,但如果不是,我想返回某种形式的错误(在下面的示例中只是一个 Err<String>)。

我正在尝试学习避免可变性并在适当的地方使用函数式样式,如果需要的话,上面的函数式样式很简单。这是我在这种情况下想到的:

fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
    sv.iter()
        .map(|s| s.1.parse::<i32>()
                    .map_err(|_e| "*** Invalid data.".to_string()))
        .collect()
}

但是,小问题是我想为每个 无效值(而不仅仅是第一个)打印一条错误消息,并且错误消息应该同时包含行号和字符串有问题的元组中的值。

我已经设法用下面的代码做到了:

fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
    sv.iter()
        .map(|s| (s.0, s.1.parse::<i32>()
                  .or_else(|e| {
                      eprintln!("ERROR: Invalid data value at line {}:  '{}'",
                                s.0, s.1);
                      Err(e)
                  })))
        .collect::<Vec<(u32, Result<i32, _>)>>() // Collect here to avoid short-circuit
        .iter()
        .map(|i| i.1
             .clone()
             .map_err(|_e| "*** Invalid data.".to_string()))
        .collect()
}

这行得通,但看起来相当困惑和麻烦 - 尤其是输入的 collect()在中间以避免短路,以便打印所有错误。 clone() call 也很烦人,我不太确定为什么需要它——编译器说我要移出借来的内容,否则,但我不太确定移动了什么。有没有办法可以做得更干净?或者我应该回到更程序化的风格?当我尝试时,我得到了可变变量和一个指示成功和失败的标志,这看起来不太优雅:

fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
    let mut datavals = Vec::new();
    let mut success = true;
    for s in sv {
        match s.1.parse::<i32>() {
            Ok(v) => datavals.push(v),
            Err(_e) => {
                eprintln!("ERROR: Invalid data value at line {}:  '{}'",
                          s.0, s.1);
                success = false;
            },
        }
    }
    if success {
        return Ok(datavals);
    } else {
        return Err("*** Invalid data.".to_string());
    }
}

有人可以建议我最好的方法吗?我应该坚持这里的程序风格吗?如果是的话,可以改进吗?还是有更简洁的功能方法来做到这一点?还是两者的混合?任何建议表示赞赏。

最佳答案

我认为这就是partition_map()来自 itertools 用于:

use itertools::{Either, Itertools};

fn data_vals<'a>(sv: &[&'a str]) -> Result<Vec<i32>, Vec<(&'a str, std::num::ParseIntError)>> {
    let (successes, failures): (Vec<_>, Vec<_>) =
        sv.iter().partition_map(|s| match s.parse::<i32>() {
            Ok(v) => Either::Left(v),
            Err(e) => Either::Right((*s, e)),
        });
    if failures.len() != 0 {
        Err(failures)
    } else {
        Ok(successes)
    }
}

fn main() {
    let numbers = vec!["42", "aaaezrgggtht", "..4rez41eza", "55"];
    println!("{:#?}", data_vals(&numbers));
}

关于functional-programming - 有没有更好的功能方法来处理带有错误检查的向量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54373540/

相关文章:

arrays - 如何在 Rust 1.0 中的堆上分配数组?

asynchronous - 为什么async-std的task::spawn阻止了后续代码行的执行?

macros - 如何使用 Rust 中的宏生成复杂的枚举变体

当静态类型为 Map 时,Scala SortedMap.map 方法返回未排序的 map

java - Java 8 是否提供了访问者模式的替代方案?

data-structures - 纯函数式数据结构有什么好处?

rust - 如何将std::sync::mpsc::Sender <T>传递给warp中的处理程序?

filter - DrRacket/Scheme 中的 map 、过滤器、文件夹

swift - 如何封装 Lazy Swift 函数的组合?

rust - 是否有一种紧凑且惯用的方式来打印错误并返回而不返回错误?