闭包递归除了不动点结构体的另一种实现方法

原文约1300字,阅读约需4分钟。发表于:

最近在看wasm的文档,其中有一个章节讲request_animation_frame的。 要知道JavaScript的这个API传统上都是无限递归调用的(因为没有返回值,所以一般来说不会因为无限递归而爆栈)。 但没想到在rust_wasm的范例里竟然强行用闭包实现了递归,不同于其他rust的闭包递归那样构造不动点结构体,而是用Rc<RefCell<Option<_>>>来构造循环引用以实现闭包递归。 我把wasm的Closure结构体替换成了Box<dyn Fn(..) -> ..>,于是发现居然能通过这种奇技淫巧来实现闭包递归。 传统不动点结构体法:(来自知乎 酱紫君 的回答) fn main() { struct Y<'yy> { fix: &'yy dyn Fn(&Y, u32) -> u32, } let fibonacci = { let yc = Y { fix: &|y, n| if n <= 1 { n } else { (y.fix)(y, n - 1) + (y.fix)(y, n - 2) } }; move |i| (yc.fix)(&yc, i) }; let factorial = { let yc = Y { fix: &|y, n| if n == 0 { 1 } else { n * (y.fix)(y, n - 1) } }; move |i| (yc.fix)(&yc, i) }; assert_eq!(fibonacci(7), 13); assert_eq!(factorial(7), 5040); } Rc/RefCell循环引用法: use std::cell::RefCell; use std::rc::Rc; fn main() { let f = Rc::new(RefCell::new(None)); let g: Rc<RefCell<Option<Box<dyn Fn(u32) -> u32>>>> = f.clone(); *g.borrow_mut() = Some(Box::new(move |i| if i <= 1 { 1 } else { i * f.borrow().as_ref().unwrap()(i - 1) } )); assert_eq!(g.borrow().as_ref().unwrap()(5), 120); } 循环引用法把rust最常见的几个甲Rc<RefCell<Option<Box<dyn...全都叠上了,讲不好和不动点法哪个更繁琐,不过wasm那个范例里循环引用还可以从外部捕获变量进行无参数递归,感觉更神奇了。 (注:都不推荐使用,递归还是建议用最基础外部fn函数来定义最好)

最近在看wasm的文档,其中有一个章节讲request_animation_frame的。传统上JavaScript的这个API是无限递归调用的。但在rust_wasm的范例中,使用了闭包来实现递归。通过替换wasm的Closure结构体,发现可以通过这种方法实现闭包递归。另外,循环引用法也可以实现闭包递归,使用了Rc和RefCell来构造循环引用。循环引用法可以从外部捕获变量进行无参数递归。不过,不推荐使用这些方法,建议使用最基础的外部fn函数来定义递归。

相关推荐 去reddit讨论