rust 是否有任何内置的方式来“合并”两个选项?

3z6pesqy  于 2023-08-05  发布在  其他
关注(0)|答案(8)|浏览(99)

在下面的示例程序中,有没有什么方法可以避免定义map2

fn map2<T, U, V, F: Fn(T, U) -> V>(f: F, a: Option<T>, b: Option<U>) -> Option<V> {
    match a {
        Some(x) => match b {
            Some(y) => Some(f(x, y)),
            None => None,
        },
        None => None,
    }
}

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| {
        a + b
    };
    let res = map2(f, a, b);
    println!("{:?}", res);
    // prints Some(15)
}

字符串
对于也会说Haskell的人来说,我想这个问题也可以表述为“在Rust中,我们可以使用什么工具来代替liftM2?”“

ghg1uchk

ghg1uchk1#

我偶然发现了这个线程,并没有找到基于zip的最明显、最直接的一行程序解决方案。

let one = Some(1);
let two = Some(2);
let sum = one.zip(two).map(|(a, b)| a + b);
assert_eq!(sum, Some(3));

let two: Option<i32> = None;
let sum = one.zip(two).map(|(a, b)| a + b);
assert_eq!(sum, None);

字符串
还有一个zip_with变体,现在标记为 unstable

let sum = one.zip_with(two, |a, b| a + b);

xfb7svmp

xfb7svmp2#

其中的几个解决方案,现在与impl OptionExtension。改编自Shepmaster提出的解决方案:

pub trait OptionExtension<T> {
    fn combine_with<U, R, F>(self, other: Option<U>, f: F) -> Option<R>
    where
        F: Fn(T, U) -> R;
    
    fn combine_with_sum<U, R>(self, other: Option<U>) -> Option<R>
    where
        T: std::ops::Add<U, Output = R>;

    fn combine_with_mul<U, R>(self, other: Option<U>) -> Option<R>
    where
        T: std::ops::Mul<U, Output = R>;
}

impl<T> OptionExtension<T> for Option<T> {
    fn combine_with<U, R, F>(self, other: Option<U>, f: F) -> Option<R>
    where
        F: Fn(T, U) -> R,
    {
        self.zip(other).map(|(x, y)| f(x, y))
    }

    fn combine_with_sum<U, R>(self, other: Option<U>) -> Option<R>
    where 
        T: std::ops::Add<U, Output = R>
    {
        let sum = |a, b| {a + b};
        self.combine_with(other, sum)
    }

    fn combine_with_mul<U, R>(self, other: Option<U>) -> Option<R>
    where 
        T: std::ops::Mul<U, Output = R>
    {
        let mul = |a, b| {a * b};
        self.combine_with(other, mul)
    }
}

字符串
还可以添加其他操作,例如fn合并_with sub、div、…
参见Rust Playground
main()函数:

fn main() {
    let a: Option<f64> = Some(5.0);
    let b: Option<f64> = Some(10.0);
    let c: Option<f64> = None;

    let result_sum_ab = a.combine_with_sum(b);
    let result_sub_ab = a.combine_with_sub(b);
    let result_mul_ab = a.combine_with_mul(b);
    let result_div_ab = a.combine_with_div(b);

    assert_eq!(result_sum_ab, Some(15.0));
    assert_eq!(result_sub_ab, Some(-5.0));
    assert_eq!(result_mul_ab, Some(50.0));
    assert_eq!(result_div_ab, Some(0.5));

    println!("result_sum_ab: {:?}", result_sum_ab);
    println!("result_sub_ab: {:?}", result_sub_ab);
    println!("result_mul_ab: {:?}", result_mul_ab);
    println!("result_div_ab: {:?}", result_div_ab);

    let result_sum_ac = a.combine_with_sum(c);
    let result_mul_ac = a.combine_with_mul(c);

    assert_eq!(result_sum_ac, None);
    assert_eq!(result_mul_ac, None);

    println!("result_sum_ac: {:?}", result_sum_ac);
    println!("result_mul_ac: {:?}", result_mul_ac);
}


输出:

result_sum_ab: Some(15.0)
result_sub_ab: Some(-5.0)
result_mul_ab: Some(50.0)
result_div_ab: Some(0.5)
result_sum_ac: None
result_mul_ac: None

tcomlyy6

tcomlyy63#

我不相信有一个直接的函数等价于liftM2,但是你可以像这样合并Option::and_thenOption::map

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| {
        a + b
    };

    println!("{:?}", a.and_then(|a| b.map(|b| f(a, b))));
}

字符串
输出量:

Some(15)

oyt4ldly

oyt4ldly4#

从Rust 1.46.0开始,你可以使用Option::zip

fn map2<T, U, V, F: Fn(T, U) -> V>(f: F, a: Option<T>, b: Option<U>) -> Option<V> {
    match a.zip(b) {
        Some((x, y)) => Some(f(x, y)),
        None => None,
    }
}

字符串
这可以与Option::map组合,如其他答案所示:

fn map2<T, U, V, F: Fn(T, U) -> V>(f: F, a: Option<T>, b: Option<U>) -> Option<V> {
    a.zip(b).map(|(x, y)| f(x, y))
}

w7t8yxp5

w7t8yxp55#

我不知道你是否能找到一条线(编辑:哦,接受的答案很好地将其减少到一行),但您可以通过匹配元组来避免嵌套的match

let a = Some(5);
let b = Some(10);
let f = |a, b| {
    a + b
};
let res = match (a, b) {
    (Some(a), Some(b)) => Some(f(a, b)),
    _ => None,
};
println!("{:?}", res);
// prints Some(15)

字符串

bybem2ql

bybem2ql6#

您可以将immediately invoked function expression (IIFE)?(try)运算符结合使用:

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| a + b;

    let res = (|| Some(f(a?, b?)))();

    println!("{:?}", res);
}

字符串
在未来,你可以使用 try blocks

#![feature(try_blocks)]

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |a, b| a + b;

    let res: Option<_> = try { f(a?, b?) };

    println!("{:?}", res);
}


参见:

l3zydbqr

l3zydbqr7#

let num_maybe = Some(5);
let num_maybe2 = Some(10);
let f = |a, b| {
    a + b
};

字符串

选项一

if let (Some(a), Some(b)) = (num_maybe, num_maybe2) {
    f(a, b)
}

选项二

num_maybe.and_then(|a| num_maybe2.map(|b| f(a, b))

选项三

[num_maybe, num_maybe2].into_iter().flatten().fold(0, f)

irtuqstp

irtuqstp8#

您可以使用Option s可以迭代的事实。迭代这两个选项,将它们压缩在一起,并将结果迭代器Map到函数上。

fn main() {
    let a = Some(5);
    let b = Some(10);
    let f = |(a, b)| {
        a + b
    };
    let res = a.iter().zip(b.iter()).map(f).next();
    println!("{:?}", res);
    // prints Some(15)
}

字符串
这需要修改f,因此参数被合并为单个元组参数。如果不修改f,直接Map到|args| f.call(args)上也是可能的,但这样就必须Map到specify the closure kind of f

相关问题