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