2013-06-05 47 views
21

Đây là một ví dụ rất đơn giản, nhưng làm thế nào tôi sẽ làm một cái gì đó tương tự như:Có thể đóng cửa đệ quy trong Rust không?

let fact = |x: u32| { 
    match x { 
     0 => 1, 
     _ => x * fact(x - 1), 
    } 
}; 

Tôi biết rằng ví dụ cụ thể này có thể dễ dàng thực hiện với sự lặp lại, nhưng tôi đang tự hỏi nếu nó có thể làm cho một đệ quy trong Rust cho những thứ phức tạp hơn (chẳng hạn như duyệt qua cây) hoặc nếu tôi được yêu cầu sử dụng chồng của riêng mình thay thế.

+1

Niko Matsakis đã viết một [bài đăng tuyệt vời] (http://smallcultfollowing.com/babysteps/blog/2013/04/30/the-case-of-the-recurring-closure/) về cách bạn có thể có khả năng (ab) sử dụng đệ quy trong đóng cửa ngay bây giờ và lý do tại sao điều này chắc chắn sẽ được loại bỏ (nếu nó không phải là đã có trong 'đến'). Tất nhiên bạn luôn có thể định nghĩa một hàm gọi chính nó, nhưng nó sẽ không nắm bắt các biến bên ngoài. –

Trả lời

20

Có một số cách để thực hiện việc này.

Bạn có thể đặt các bao đóng vào một cấu trúc và chuyển cấu trúc này đến phần đóng. Bạn thậm chí có thể định nghĩa inline struct trong một hàm:

fn main() { 
    struct Fact<'s> { f: &'s Fn(&Fact, u32) -> u32 } 
    let fact = Fact { 
     f: &|fact, x| if x == 0 {1} else {x * (fact.f)(fact, x - 1)} 
    }; 

    println!("{}", (fact.f)(&fact, 5)); 
} 

này được xung quanh các vấn đề về việc có loại vô hạn (một chức năng mà mất chính nó như là một đối số) và các vấn đề mà fact chưa được định nghĩa bên trong đóng cửa chính nó khi một người viết let fact = |x| {...} và do đó người ta không thể tham khảo nó ở đó.

Điều này hoạt động trong Rust 1.17, nhưng có thể có thể được làm bất hợp pháp trong tương lai vì nó nguy hiểm trong một số trường hợp, như được nêu trong the blog post The Case of the Recurring Closure. Nó hoàn toàn an toàn ở đây vì không có đột biến.


lựa chọn khác là chỉ cần viết một hàm đệ quy như là một mục fn, mà cũng có thể được định nghĩa nội tuyến trong một hàm:

fn main() { 
    fn fact(x: u32) -> u32 { if x == 0 {1} else {x * fact(x - 1)} } 

    println!("{}", fact(5)); 
} 

này hoạt động tốt nếu bạn không cần phải nắm bắt bất cứ điều gì từ môi trường.


Một tùy chọn khác là sử dụng giải pháp mục fn nhưng chuyển rõ ràng args/môi trường bạn muốn.

fn main() { 
    struct FactEnv { base_case: u32 } 
    fn fact(env: &FactEnv, x: u32) -> u32 { 
     if x == 0 {env.base_case} else {x * fact(env, x - 1)} 
    } 

    let env = FactEnv { base_case: 1 }; 
    println!("{}", fact(&env, 5)); 
} 

Tất cả những công việc này với Rust 1.17 và có thể đã hoạt động kể từ phiên bản 0.6. Các định nghĩa của fn bên trong fn s không khác với những gì được xác định ở cấp cao nhất, ngoại trừ chúng chỉ có thể truy cập trong số fn chúng được xác định bên trong.

Các vấn đề liên quan