簡體   English   中英

如何在 Rust 中使用 FnMut 實現 Y-Combinator?

[英]How can I implement Y-Combinator with FnMut in Rust?

我找到了一個支持 Fn 的 Y-Combinator 的實現 但是我想要一個 FnMut 版本。

但是, FnMut 不能包裝到Rc中,所以我將它們包裝在Rc<RefCell>中。 以下代碼可以遞歸方式使a[i] = i 這是我發現的最簡單的代碼,用於測試我們是否可以在閉包中調用自身。

// test code part

let mut a = vec![0; 5];
let n = a.len();

y_mut(Rc::new(RefCell::new(|f: Rc<RefCell<dyn FnMut(usize)>>| {
    move |i| {
        if i < n {
            a[i] = i;
            ((*f).borrow_mut())(i + 1);
        }
    }
})))(0);

println!("a is {:?}", a);

這是我的y_mut版本,源自演示。

fn y_mut<A, O, F>(f: Rc<RefCell<dyn FnMut(Rc<RefCell<dyn FnMut(A) -> O>>) -> F>>) -> impl FnMut(A) -> O
    where
        F: FnMut(A) -> O,
        F: 'static,
        A: 'static,
        O: 'static,
{
    struct X<F>(Rc<RefCell<dyn FnMut(X<F>) -> F>>);

    impl<F> Clone for X<F> {
        fn clone(&self) -> Self {
            Self(Rc::clone(&self.0))
        }
    }

    impl<F> X<F> {
        fn call(&self, x: Self) -> F {
            ((*self.0).borrow_mut())(x)
        }
    }

    let f = Rc::new(RefCell::new(move |x: X<F>| {
        let mut ff = (*f).borrow_mut();
        ff(Rc::new(RefCell::new(move |a| (x.call(x.clone()))(a))))
    }));
    let x = X(f);
    (|x: X<F>| x.call(x.clone()))(x)
}

但是,此代碼無法編譯,因為|f: Rc<RefCell<dyn FnMut(usize)>>| 只實現 FnOnce,而不是 FnMut。 我想知道如何解決這個問題?

測試代碼閉包中的move使其也移動a閉包中。 您可以a引用向量並移動來防止這種情況:

y_mut(Rc::new(RefCell::new(|f: Rc<RefCell<dyn FnMut(usize)>>| {
    let a = &mut a;
    move |i| {
        ...
    }
})))(0);

但隨后閉包不再滿足'static界限,因為它現在從其環境中借用了一些東西。 您可以通過使用另一個Rc來解決這個問題,並將其克隆到外部和內部閉包中。 此版本的測試代碼編譯:

fn main() {
    let a = Rc::new(RefCell::new(vec![0; 5]));
    let n = a.borrow().len();

    y_mut(Rc::new(RefCell::new({
        let a = Rc::clone(&a);
        move |f: Rc<RefCell<dyn FnMut(usize)>>| {
            let a = Rc::clone(&a);
            move |i| {
                if i < n {
                    a.borrow_mut()[i] = i;
                    f.borrow_mut()(i + 1);
                }
            }
        }
    })))(0);

    println!("a is {:?}", a.borrow());
}

操場

當然,這樣做意味着您不需要FnMut開始,因此 Y 組合器實現可以簡化回您開始時的非 mut 版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM