有没有更好的功能方法来处理带有错误检查的向量? [英] Is there a better functional way to process a vector with error checking?

查看:62
本文介绍了有没有更好的功能方法来处理带有错误检查的向量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm learning Rust and would like to know how I can improve the code below.

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

I have a vector of tuples of form (u32, String). The u32 values represent line numbers and the Strings are the text on the corresponding lines. As long as all the String values can be successfully parsed as integers, I want to return an Ok<Vec<i32>> containing the just parsed String values, but if not I want to return an error of some form (just an Err<String> in the example below).

我正在尝试学习避免可变性,并在适当的地方使用功能样式,如果需要的话,以上内容就可以直接在功能上进行.这是我在这种情况下想到的:

I'm trying to learn to avoid mutability and use functional styles where appropriate, and the above is straightforward to do functionally if that was all that was needed. Here's what I came up with in this case:

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()
}

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

However, the small catch is that I want to print an error message for every invalid value (and not just the first one), and the error messages should contain both the line number and the string values in the offending tuple.

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

I've managed to do it with the following code:

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()调用也很烦人,我不太确定为什么需要它-编译器说我要移出借来的内容,但是我真的不确定要移什么.有没有办法可以更干净地完成它?还是应该回到一种更具程序性的风格?当我尝试时,我最终得到了可变变量和一个标志来指示成功和失败,这似乎不太优雅:

This works, but seems rather messy and cumbersome - especially the typed collect() in the middle to avoid short-circuiting so all the errors are printed. The clone() call is also annoying, and I'm not really sure why it's needed - the compiler says I'm moving out of borrowed content otherwise, but I'm not really sure what's being moved. Is there a way it can be done more cleanly? Or should I go back to a more procedural style? When I tried, I ended up with mutable variables and a flag to indicate success and failure, which seems less elegant:

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());
    }
}

有人可以建议我这样做的最佳方法吗?我应该在这里坚持程序风格吗?如果可以,可以改进吗?还是有一种更清洁的功能性方法?还是两者的融合?任何建议表示赞赏.

Can someone advise me on the best way to do this? Should I stick to the procedural style here, and if so can that be improved? Or is there a cleaner functional way to do it? Or a blend of the two? Any advice appreciated.

推荐答案

在纯粹的功能样式中,您必须避免副作用. 打印错误是一个副作用.首选样式是返回样式的对象:

In a purely functional style, you have to avoid side-effects. Printing errors is a side-effect. The preferred style would be to return an object of the style:

Result<Vec<i32>, Vec<String>>

,并在data_vals函数返回后打印列表.

and print the list after the data_vals function returns.

因此,从本质上讲,您希望您的处理过程收集一个整数列表和一个字符串列表:

So, essentially, you want your processing to collect a list of integers, and a list of strings:

fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, Vec<String>> {
    let (ok, err): (Vec<_>, Vec<_>) = sv
        .iter()
        .map(|(i, s)| {
            s.parse()
                .map_err(|_e| format!("ERROR: Invalid data value at line {}: '{}'", i, s))
        })
        .partition(|e| e.is_ok());

    if err.len() > 0 {
        Err(err.iter().filter_map(|e| e.clone().err()).collect())
    } else {
        Ok(ok.iter().filter_map(|e| e.clone().ok()).collect())
    }
}

fn main() {
    let input = vec![(1, "0".to_string())];
    let r = data_vals(&input);
    assert_eq!(r, Ok(vec![0]));

    let input = vec![(1, "zzz".to_string())];
    let r = data_vals(&input);
    assert_eq!(r, Err(vec!["ERROR: Invalid data value at line 1: 'zzz'".to_string()]));
}

游乐场链接

这使用了partition,它不依赖于外部包装箱.

This uses partition which does not depend on an external crate.

这篇关于有没有更好的功能方法来处理带有错误检查的向量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆