首页
学习
活动
专区
圈层
工具
发布

防锈药
EN

Stack Overflow用户
提问于 2021-04-05 00:38:11
回答 3查看 1.2K关注 0票数 1

作为一个有抱负的沙沙主义者,我一直在努力阅读Rust编程语言书,在第13章中,我试图概括Cacher结构,它的目的是围绕闭包实现懒惰的评估。虽然我能够使用泛型将闭包签名推广到任何一个具有任何输出类型的参数,但我不知道如何将其推广到具有任意数量的params的闭包。我觉得应该有办法这样做。

代码语言:javascript
复制
struct Cacher<'a, Args, V: Clone>  
{
    calculation: &'a dyn Fn(Args) -> V,
    value: Option<V>
}

impl<'a, Args, V: Clone> Cacher<'a, Args, V>
{
    fn new(calculation: &'a dyn Fn(Args) -> V) -> Cacher<Args, V> {
        Cacher {
            calculation: calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: Args) -> V {
        // all this cloning is probably not the best way to do this
        match self.value.clone() {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v.clone());
                v
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let mut cached_func = Cacher::new(&(|asd| asd + 1));
        assert_eq!(cached_func.value(1), 2);
        assert_eq!(cached_func.value(4), 2);
    }

    #[test]
    fn it_works_too() {
        // compiler hates this
        let mut cached_func = Cacher::new(&(|asd, qwe| asd + qwe));
        assert_eq!(cached_func.value(1, 1), 2);
        assert_eq!(cached_func.value(4, 1), 2);
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-04-06 18:57:46

您可以在夜间使用特征 (和密切相关的闭包)特性来完成这一任务。这允许您像Fn一样使用Fn<Args, Output = V>,其中Args是传递给函数的所有参数的元组类型。

代码语言:javascript
复制
#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Cacher<'a, Args, V: Clone>  
{
    calculation: &'a dyn Fn<Args, Output = V>,
    value: Option<V>
}

impl<'a, Args, V: Clone> Cacher<'a, Args, V>
{
    fn new(calculation: &'a dyn Fn<Args, Output = V>) -> Cacher<Args, V> {
        Cacher {
            calculation: calculation,
            value: None,
        }
    }

    fn value(&mut self, args: Args) -> V {
        // all this cloning is probably not the best way to do this
        match self.value.clone() {
            Some(v) => v,
            None => {
                let v = self.calculation.call(args);
                self.value = Some(v.clone());
                v
            }
        }
    }
}

这需要您使用元组调用value()

代码语言:javascript
复制
let mut cache1 = Cacher::new(&|a| a + 1);
let value1 = cache1.value((7,));

let mut cache2 = Cacher::new(&|a, b| a + b);
let value2 = cache2.value((7, 8));

但是,如果您愿意为众多元组类型制作样板,则可以更好地使用:

代码语言:javascript
复制
impl<'a, T, V: Clone> Cacher<'a, (T,), V>
{
    fn value2(&mut self, arg1: T) -> V {
        self.value((arg1, ))
    }
}

impl<'a, T, U, V: Clone> Cacher<'a, (T, U), V>
{
    fn value2(&mut self, arg1: T, arg2: U) -> V {
        self.value((arg1, arg2))
    }
}

// ...

let mut cache1 = Cacher::new(&|a: usize| a + 1);
let value1 = cache1.value2(7);

let mut cache2 = Cacher::new(&|a: usize, b: usize| a + b);
let value2 = cache2.value2(7, 8);

看到它在游乐场上运行。

这只在夜间工作,因为如果这是将来一般支持的话,它还没有稳定下来。

票数 2
EN

Stack Overflow用户

发布于 2021-04-05 10:43:06

在铁锈中,函数没有可变数量的参数,除非在某些情况下与C兼容。这个答案提供了更多的背景。

在您的示例中,您可以使用懒惰静态机箱实现一些通用的懒惰评估。您不会将关闭传递给这个板条箱,至少不会显式地传递。但是,您将闭包的主体放在一个变量中,该变量在第一次访问时由惰性静态计算(有点像接受()的闭包,如果愿意的话,其结果将存储在Cacher中)。

票数 1
EN

Stack Overflow用户

发布于 2021-04-05 20:57:20

很难理解你到底需要什么。所以我猜:

代码语言:javascript
复制
struct Cacher<'a, Args, V: Copy>
{
    calculation: &'a dyn Fn(Args) -> V,
    value: Option<V>
}

impl<'a, Args, V: Copy> Cacher<'a, Args, V>
{
    fn new(calculation: &'a dyn Fn(Args) -> V) -> Cacher<Args, V> {
        Cacher {
            calculation: calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: Args) -> V {
        // Cloning fixed
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let mut cached_func = Cacher::new(&(|asd| asd + 1));
        assert_eq!(cached_func.value(1), 2);
        assert_eq!(cached_func.value(4), 2);
    }

    #[test]
    fn it_works_too() {
        // The compiler is fine
        // Although now, it's not multiple arguments but rather one arg, acting as many
        let mut cached_func = Cacher::new(&(|asd: (usize, usize)| asd.0 + asd.1));
        assert_eq!(cached_func.value((1, 1)), 2);
        assert_eq!(cached_func.value((4, 1)), 2);
    }
}

请记住,Rust的泛型可以被视为代数数据类型,因此,只允许使用enumsstructs和函数(如果您认为它们与函数不同,也可以使用闭包)。第二个测试有效,因为元组可以被认为是structs

因此,在一个函数定义中不可能有多个参数。

解决这个问题的通常方法是使用宏。虽然方法宏还不存在于铁锈中。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66947056

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档