生锈的功能组成链

发布于 2025-02-06 23:51:34 字数 2371 浏览 2 评论 0 原文

我想在Rust的对象方法链中实现功能组成

在此之前,我确认在RUST中实现了功能应用程序“管道”的对象方法链,如下所示:

https://docs.rs/apply/0.2.2/apply/trait/trait.apply.html

trait Pipe: Sized {
    fn pipe<F, B>(self, f: F) -> B
    where
        F: FnOnce(Self) -> B,
    {
        f(self)
    }
}

impl<T> Pipe for T {}
 
fn main() {
    let string = 1.pipe(|x| x * 2).pipe(|x| x + 1).pipe(|x| x.to_string());

    println!("{}", string);
}


现在,功能构成守则是 如何在Rust中撰写功能

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

let composed_f = (|x| x * 2).compose(|x| x + 1).compose(|x| x.to_string());

适当的代码在“管道”之类的生锈中实现功能组成


编辑

功能组成宏引用 https://stackoverflow.com/a/a/45792463/113166608

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

I want to implement function composition in an object method chain in Rust.

Before that, I confirmed that implementation of an object method chain for "pipe" of function application is possible in Rust as follows:

https://docs.rs/apply/0.2.2/apply/trait.Apply.html

trait Pipe: Sized {
    fn pipe<F, B>(self, f: F) -> B
    where
        F: FnOnce(Self) -> B,
    {
        f(self)
    }
}

impl<T> Pipe for T {}
 
fn main() {
    let string = 1.pipe(|x| x * 2).pipe(|x| x + 1).pipe(|x| x.to_string());

    println!("{}", string);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6210a499e2522ff04e0cbbeef8cedc5


Now, the code of function composition is
How to compose functions in Rust?

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

So the goal would be

let composed_f = (|x| x * 2).compose(|x| x + 1).compose(|x| x.to_string());

and what is the proper code to implement function composition in an object method chain in Rust like "pipe"?


EDIT

for function composition macro quoted from
https://stackoverflow.com/a/45792463/11316608

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

み格子的夏天 2025-02-13 23:51:34

直接在封闭式上实施此功能是棘手的,但是我们可以使用代表一个可合并功能的newType使其变得非常简单。这添加了一个步骤,将转换为可合并的功能,然后在最后再次出现,但是结束时很合理(尤其是考虑到其表现力),它不需要拳击或其他巧妙的技巧,并且实现非常容易理解。

因为它与未包装的,嵌套的闭合调用结合起来,因此编译器也很有可能能够优化所有构图调用,并将封闭式互相关闭,甚至可能进入最终的呼叫站点。

use std::marker::PhantomData;

struct ComposeableFn<F, A, B>(F, PhantomData<*mut A>, PhantomData<*mut B>);

impl<F, A, B> ComposeableFn<F, A, B>
    where F: FnMut(A) -> B
{
    pub fn new(f: F) -> Self {
        Self(f, Default::default(), Default::default())
    }
    
    pub fn compose<C>(self, mut next: impl FnMut(B) -> C)
        -> ComposeableFn<impl FnMut(A) -> C, A, C>
    {
        let mut prior = self.0;
        
        ComposeableFn::new(move |v| next(prior(v)))
    }
    
    pub fn into_inner(self) -> F {
        self.0
    }
}

像这样使用:

fn main() {
    let mut composed_f = ComposeableFn::new(|x: i32| x * 2)
        .compose(|x| x + 1)
        .compose(|x| x.to_string())
        .into_inner();
        
    println!("{:?}", composed_f(21)); // "43"
}


phantomdata 成员必须允许实现命名 a b ,否则它们将被视为未使用。我选择*mut _ ,因为这使得 a b 中的类型不变。


您可以用宏来使构图的表达更好:

macro_rules! compose {
    ({ $fh:expr } $( { $ft:expr } )*) => {
        ComposeableFn::new($fh)
        $( .compose($ft) )*
        .into_inner()
    }
}

fn main() {
    let mut composed_f = compose!(
        {|x: i32| x * 2}
        {|x| x + 1}
        {|x| x.to_string()}
    );
        
    println!("{:?}", composed_f(21));
}

Implementing this directly on closures is tricky, but we can make it vastly simpler with a newtype that represents a composeable function. This adds a step to convert into a composeable function and then out again at the end, but it winds up being pretty reasonable (especially considering its expressive power), it requires no boxing or other clever tricks, and the implementation is very easy to understand.

Because it winds up with unboxed, nested closure invocations, it is also very likely that the compiler will be able to optimize away all of the composition calls and inline the closures into each other, likely even into the final call site.

use std::marker::PhantomData;

struct ComposeableFn<F, A, B>(F, PhantomData<*mut A>, PhantomData<*mut B>);

impl<F, A, B> ComposeableFn<F, A, B>
    where F: FnMut(A) -> B
{
    pub fn new(f: F) -> Self {
        Self(f, Default::default(), Default::default())
    }
    
    pub fn compose<C>(self, mut next: impl FnMut(B) -> C)
        -> ComposeableFn<impl FnMut(A) -> C, A, C>
    {
        let mut prior = self.0;
        
        ComposeableFn::new(move |v| next(prior(v)))
    }
    
    pub fn into_inner(self) -> F {
        self.0
    }
}

Used like so:

fn main() {
    let mut composed_f = ComposeableFn::new(|x: i32| x * 2)
        .compose(|x| x + 1)
        .compose(|x| x.to_string())
        .into_inner();
        
    println!("{:?}", composed_f(21)); // "43"
}

(Playground)


The PhantomData members are required to allow the implementation to name A and B, otherwise they would be considered unused. I chose *mut _ as this makes the type invariant in A and B.


You can make the expression of a composition a bit nicer with a macro:

macro_rules! compose {
    ({ $fh:expr } $( { $ft:expr } )*) => {
        ComposeableFn::new($fh)
        $( .compose($ft) )*
        .into_inner()
    }
}

fn main() {
    let mut composed_f = compose!(
        {|x: i32| x * 2}
        {|x| x + 1}
        {|x| x.to_string()}
    );
        
    println!("{:?}", composed_f(21));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文