Functional programming 如何在Rust中编写函数?

Functional programming 如何在Rust中编写函数?,functional-programming,rust,Functional Programming,Rust,我试图写一个由两个函数组成的函数。最初的设计非常简单:一个函数接受两个函数并返回一个组合函数,然后我可以与其他函数组合,因为Rust没有rest参数。我遇到了一堵墙,墙上堆满了令人沮丧的无用的编译器错误 我的写作功能: fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a> where F: 'a + Fn(A) -> B + Sized, G: 'a + Fn(

我试图写一个由两个函数组成的函数。最初的设计非常简单:一个函数接受两个函数并返回一个组合函数,然后我可以与其他函数组合,因为Rust没有rest参数。我遇到了一堵墙,墙上堆满了令人沮丧的无用的编译器错误

我的写作功能:

fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
where
    F: 'a + Fn(A) -> B + Sized,
    G: 'a + Fn(B) -> C + Sized,
{
    Box::new(move |x| g(f(x)))
}
编译器不喜欢这样,不管我怎么做,特性边界永远不会满足。错误是:

error[E0277]:编译时无法知道'dyn std::ops::Fn()->`类型的值的大小
-->src/main.rs:13:19
|
13 |设finally=compose(*addAndMultiply,*divideAndSubtract);
|^^^^^^在编译时没有已知的大小
|
=help:trait`std::marker::Sized`未为`dyn std::ops::Fn(41;)实现_`
=注意:要了解更多信息,请访问
注:"撰写"所需`
-->src/main.rs:1:1
|
1 |/fn组成
2 | |在哪里
3 | | F:'a+Fn(a)->B+尺寸,
4 | | G:'a+Fn(B)->C+尺寸,
5  | | {
6 | |盒::新(移动| x | g(f(x)))
7  | | }
| |_^

最后只需在
中添加引用
,它就可以工作了:

fn main() {
    let addAndMultiply = compose(|x| x * 2, |x| x + 2);
    let divideAndSubtract = compose(|x| x / 2, |x| x - 2);

    let finally = compose(&*addAndMultiply, &*divideAndSubtract);
    println!("Result is {}", finally(10));
}
取消引用
add和multiply
divide和subtract
会发现一个没有
大小的特征对象
;它需要包装在
框中或引用,以便传递给具有
大小约束的函数。

为使其工作,只需再次引用组合函数:

let finally = compose(&*multiply_and_add, &*divide_and_subtract);
(注意,在Rust中,约定要求变量名应为snake_)


但是,我们可以让它变得更好

由于Rust 1.26,我们可以使用(以前的特性为
#![feature(conservative_impl_trait)]
)。这可以帮助您大大简化示例,因为它允许您跳过生命周期、引用、
size
约束和
Box
es:

fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
    F: Fn(A) -> B,
    G: Fn(B) -> C,
{
    move |x| g(f(x))
}

fn main() {
    let multiply_and_add = compose(|x| x * 2, |x| x + 2);
    let divide_and_subtract = compose(|x| x / 2, |x| x - 2);

    let finally = compose(multiply_and_add, divide_and_subtract);
    println!("Result is {}", finally(10));
}
fn组合(f:f,g:g)->impl-fn(A)->C
哪里
F:Fn(A)->B,
G:Fn(B)->C,
{
移动| x | g(f(x))
}
fn main(){
让乘法和加法=合成(|x | x*2,|x | x+2);
让除法和减法=合成(| x | x/2,| x | x-2);
设finally=compose(乘和加,除和减);
println!(“结果是{}”,最后(10));
}

最后,由于您提到了rest参数,我怀疑您实际上想要的是有一种方法以灵活的方式链接组合任意多个函数。我写这个宏就是为了这个目的:

macro_rules! compose {
    ( $last:expr ) => { $last };
    ( $head:expr, $($tail:expr), +) => {
        compose_two($head, compose!($($tail),+))
    };
}

fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
    F: Fn(A) -> B,
    G: Fn(B) -> C,
{
    move |x| g(f(x))
}

fn main() {
    let add = |x| x + 2;
    let multiply = |x| x * 2;
    let divide = |x| x / 2;
    let intermediate = compose!(add, multiply, divide);

    let subtract = |x| x - 2;
    let finally = compose!(intermediate, subtract);

    println!("Result is {}", finally(10));
}
macro\u规则!谱写{
($last:expr)=>{$last};
($head:expr,$($tail:expr),+)=>{
组合两个($头,组合!($($尾),+))
};
}
fn组成两个(f:f,g:g)->impl fn(A)->C
哪里
F:Fn(A)->B,
G:Fn(B)->C,
{
移动| x | g(f(x))
}
fn main(){
让加法=| x | x+2;
让乘法=| x | x*2;
设除法=| x | x/2;
让中间=合成!(加、乘、除);
让减法=| x | x-2;
让finally=compose!(中间,减法);
println!(“结果是{}”,最后(10));
}
宏规则!公司{
($f:expr)=>{
移动| g:fn(|)->|移动| x:| |$f(g(x))
};
}
fn main(){
设add1=|x | x+1;
设add2=|x | x+2;
设add3=comp!(add1)(add2);
println!(“{}”,add3(3));
}

至于主要目标,您可能会寻找这样一个:不适用于我的情况。根据口味,您可能希望在参数位置使用
impl Trait
来进一步简化事情。
compose\u two
并不是严格必要的。将函数内联到宏中可以工作,但当类型不匹配时可能会产生严重的编译错误:
($head:expr,$($tail:expr),+)=>{| x | compose!($($tail),+)($head(x))}
听起来可能没有用,但是熟悉编程的人会发现,根据上面的实现,加法和乘法实际上应该称为乘法和加法,这有点违反直觉。回答得不错。这适用于具有多个arg的函数吗?虽然我猜一个人可以传递元组。Rust没有Haskell中那样好的特性,在Haskell中,多个参数相当于这些参数的一个元组。
macro_rules! compose {
    ( $last:expr ) => { $last };
    ( $head:expr, $($tail:expr), +) => {
        compose_two($head, compose!($($tail),+))
    };
}

fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
    F: Fn(A) -> B,
    G: Fn(B) -> C,
{
    move |x| g(f(x))
}

fn main() {
    let add = |x| x + 2;
    let multiply = |x| x * 2;
    let divide = |x| x / 2;
    let intermediate = compose!(add, multiply, divide);

    let subtract = |x| x - 2;
    let finally = compose!(intermediate, subtract);

    println!("Result is {}", finally(10));
}