Recursion 这就需要验证guestloop实现了Fn(i32,&Fix),但只有Fix实现了Fn(i32)(因为impl是有条件的),这才是正确的,如果guestloop实现了Fn(i32,&Fix)(因为impl也是有条件的),哪个。。。你明白了。换句话说,这里的Fn的两个实现相互依赖,编译器无法解决这一问题。

Recursion 这就需要验证guestloop实现了Fn(i32,&Fix),但只有Fix实现了Fn(i32)(因为impl是有条件的),这才是正确的,如果guestloop实现了Fn(i32,&Fix)(因为impl也是有条件的),哪个。。。你明白了。换句话说,这里的Fn的两个实现相互依赖,编译器无法解决这一问题。,recursion,rust,Recursion,Rust,从您停止的地方开始: fn fix<T, R, F>(func: fn(T, F) -> R) -> F where F: Fn(T) -> R { |val: T| func(val, fix(func)) } 我们无法将fix(func)传递到func,因为它需要F的可命名类型。我们必须满足于一个特征对象: fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> imp

从您停止的地方开始:

fn fix<T, R, F>(func: fn(T, F) -> R) -> F
    where F: Fn(T) -> R
{
    |val: T| func(val, fix(func))
}
我们无法将
fix(func)
传递到
func
,因为它需要
F
的可命名类型。我们必须满足于一个特征对象:

fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
    |val: T| func(val, &fix(func))
}
这是一个有点神秘的信息。由于默认情况下impl traits总是
'static
,这是一种迂回的说法:“闭包对于
'static
”来说活得不够长。为了获得真正的错误消息,我们将
+'static
附加到
impl Fn(T)->R
并重新编译:

闭包可能比当前函数的寿命长,但它借用了当前函数所拥有的“func”
所以这才是真正的问题。它正在借用
func
。我们不需要借用
func
,因为
fn
Copy
,所以我们可以随心所欲地复制它。让我们先用
move
来结束闭包,然后去掉前面的
+”static

fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
    move |val: T| func(val, &fix(func))
}
不幸的是,这不是免费分配的

此外,我们还可以对结果进行一点概括,以允许任意闭包和生存期:

fn fix<'a, T, R, F>(func: F) -> impl 'a + Fn(T) -> R
    where F: 'a + Fn(T, &Fn(T) -> R) -> R + Copy
{
    move |val: T| func(val, &fix(func))
}
下面演示如何使用此
fix
函数计算阶乘:

fn factorial(n: u64) -> u64 {
    // f: Lazy<u64 -> u64> -> u64 -> u64
    fn f(fac: Lazy<'static, Box<FnBox(u64) -> u64>>) -> Box<FnBox(u64) -> u64> {
        Box::new(move |n| {
            if n == 0 {
                1
            } else { 
                n * fac()(n - 1)
            }
        })
    }
    fix(f)(n)
}
fn阶乘(n:u64)->u64{
//f:Lazy u64>->u64->u64
fn f(fac:Lazy这是一个答案,它是这个问题的一个子集

λf.(λw.w w)(λw.f(w w))
中的解决方案太复杂,使用
Box
在堆中分配内存。我想简化此过程

首先,让我们将类型
Mu
作为一个特征来实现

trait Mu<T> {
    fn unroll(&self, &Mu<T>) -> T;
}
有了这个,我们现在可以按如下方式编写y组合符:

fn y<T, F: Fn(T) -> T>(f: &F) -> T {
    (&|w: &Mu<T>| w.unroll(w))(&|w: &Mu<T>| f(w.unroll(w)))
}
如上所述,让我们实现这个新特性

impl<T, R, F> Mu2<T, R> for F
where
    F: Fn(&Mu2<T, R>, T) -> R,
{
    fn unroll(&self, o: &Mu2<T, R>, t: T) -> R {
        self(o, t)
    }
}
结果:

3628800
全部完成

您可以看到
y
函数的签名与提问者的
fix
稍有不同,但这并不重要

直接循环版本

避免返回闭包的相同技术也可用于正常的直接循环版本:

fn fix<T, R, F>(f: &F, t: T) -> R
where
    F: Fn(&Fn(T) -> R, T) -> R,
{
    f(&|t| fix(f, t), t)        
}

fn fib(i: i32) -> i32 {
    let fn_ = &|f:&Fn(i32) -> i32, x| if x < 2 { x } else { f(x-1) + f(x-2) };
    fix(fn_, i)
}
没有不安全的代码,而这个版本,如果它工作的话,比上面给出的版本(O(2^N))执行得更好(O(N))

这是因为一次只能有一个对象的
&mut
。但是Y combinator的思想,甚至是定点函数,需要在调用函数时同时捕获/传递函数,这是两个引用,不能只标记其中一个而不标记另一个

另一方面,我想知道我们是否可以做一些其他语言通常无法做到但Rust似乎能够做到的事情。我在考虑将
F
的第一个参数类型从
Fn
限制为
FnOnce
(由于
y
函数将提供实现,更改为
FnMut
没有意义,我们知道它将没有状态,但更改为
FnOnce
意味着我们只希望它使用一次),此时不允许生锈,因为我们无法按值传递未大小的对象

所以基本上,这个实现是我们能想到的最灵活的解决方案

顺便说一下,不可变限制的解决方法是使用伪突变:

fn fib(i: u32) -> u32 {
    let fn_ = &|f:&Fn((u32,u32,u32)) -> u32, (x,i,j)| 
        match x {
            0 => i,
            1 => j,
            _ => {
                f((x-1,j,i+j))
            }
        };
    fix(&fn_, (i,1,1))
}

你对后一种方法怎么看?是否可以用一些
trait Fix
F
的存在定义来完成呢?@Odomontois你不能通过值返回一个闭包。我的意思是“你不能吃太阳”,而不是“你不能吃整匹马(除非你真的饿了)”有道理。再聪明的尝试也无法避免做不到这一点。你将有一次
impl Trait
是稳定的,但它不是,所以这没有帮助。fix
mo的原因我们喜欢蹦床later@Shepmaster我不知道需要
{
才能进入下一行?我的过程是复制代码,粘贴到操场上,然后运行rustfmt。这使用默认设置,据我所知,这是事实上的通用样式。具体来说,
{
一旦有了
where
子句,就会转到下一行,并且每个where子句都在一个唯一的行上。
{
在所有情况下都不在下一行。感谢您提供的信息!
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
    |val: T| func(val, &fix(func))
}
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
    move |val: T| func(val, &fix(func))
}
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> Box<Fn(T) -> R>
    where T: 'static,
          R: 'static
{
    Box::new(move |val: T| func(val, fix(func).as_ref()))
}
fn fix<'a, T, R, F>(func: F) -> impl 'a + Fn(T) -> R
    where F: 'a + Fn(T, &Fn(T) -> R) -> R + Copy
{
    move |val: T| func(val, &fix(func))
}
type Lazy<'a, T> = Box<FnBox() -> T + 'a>;

// fix: (Lazy<T> -> T) -> T
fn fix<'a, T, F>(f: F) -> T
    where F: Fn(Lazy<'a, T>) -> T + Copy + 'a
{
    f(Box::new(move || fix(f)))
}
fn factorial(n: u64) -> u64 {
    // f: Lazy<u64 -> u64> -> u64 -> u64
    fn f(fac: Lazy<'static, Box<FnBox(u64) -> u64>>) -> Box<FnBox(u64) -> u64> {
        Box::new(move |n| {
            if n == 0 {
                1
            } else { 
                n * fac()(n - 1)
            }
        })
    }
    fix(f)(n)
}
trait Mu<T> {
    fn unroll(&self, &Mu<T>) -> T;
}
impl<T, F: Fn(&Mu<T>) -> T> Mu<T> for F {
    fn unroll(&self, o: &Mu<T>) -> T {
        self(o)
    }
}
fn y<T, F: Fn(T) -> T>(f: &F) -> T {
    (&|w: &Mu<T>| w.unroll(w))(&|w: &Mu<T>| f(w.unroll(w)))
}
trait Mu2<T, R> {
    fn unroll(&self, &Mu2<T, R>, t: T) -> R;
}
impl<T, R, F> Mu2<T, R> for F
where
    F: Fn(&Mu2<T, R>, T) -> R,
{
    fn unroll(&self, o: &Mu2<T, R>, t: T) -> R {
        self(o, t)
    }
}
fn y<T, R, F>(f: &F, t: T) -> R
where
    F: Fn(&Fn(T) -> R, T) -> R,
{
    (&|w: &Mu2<T, R>, t| w.unroll(w, t))((&|w: &Mu2<T, R>, t| f(&|t| w.unroll(w, t), t)), t)
}
fn main() {
    let fac = &|f: &Fn(i32) -> i32, i| if i > 0 { i * f(i - 1) } else { 1 };
    println!("{}", y(fac, 10))
}
fn fix<T, R, F>(f: &F, t: T) -> R
where
    F: Fn(&Fn(T) -> R, T) -> R,
{
    f(&|t| fix(f, t), t)        
}

fn fib(i: i32) -> i32 {
    let fn_ = &|f:&Fn(i32) -> i32, x| if x < 2 { x } else { f(x-1) + f(x-2) };
    fix(fn_, i)
}
fn fib1(i: u32) -> u32 {
    let mut i0 = 1;
    let mut i1 = 1;
    let fn_ = &mut |f:&Fn(u32) -> u32, x| 
        match x {
            0 => i0,
            1 => i1,
            _ => {
                let i2 = i0;
                i0 = i1;
                i1 = i1 + i2;
                f(x)
            }
        };

    fix_mut(fn_, i)
}
fn fib(i: u32) -> u32 {
    let fn_ = &|f:&Fn((u32,u32,u32)) -> u32, (x,i,j)| 
        match x {
            0 => i,
            1 => j,
            _ => {
                f((x-1,j,i+j))
            }
        };
    fix(&fn_, (i,1,1))
}