2013-06-05 15 views
21

Bu çok basit bir örnektir, ama nasıl benzer bir şey yapacağını:Rust'ta tekrarlı kapatma yapmak mümkün mü?

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

Bu spesifik bir örnek kolayca yineleme ile yapılabilir biliyorum, ama özyinelemeli yapmak mümkün olup olmadığını merak ediyorum Rust'ta daha karmaşık şeyler (örn. ağaçların kesilmesi) için işlev veya kendi yığınımı kullanmam gerekirse.

+1

Niko Matsakis'in nasıl potansiyel can hakkında [harika yazı] (http://smallcultfollowing.com/babysteps/blog/2013/04/30/the-case-of-the-recurring-closure/) (ab yazdı) şu anda kapanışlarda özyinelemeyi ve bunun neden en çok niçin kaldırılacağını (eğer zaten 'gelen' değilse). Tabii ki, her zaman kendini çağıran bir işlevi tanımlayabilirsiniz, ancak dış değişkenleri yakalamaz. –

cevap

20

Bunu yapmanın birkaç yolu vardır.

Bir yapıya kapaklar koyabilir ve bu yapıyı kapamaya geçirebilirsiniz. Hatta bir işlevde yapılar satır içi tanımlayabilirsiniz:

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

Bu fact henüz kapanması içine tanımlı olmadığı sonsuz bir türünü (bağımsız değişken olarak kendini alır bir işlevi) ve sorunu olmasının sorunu alır Biri let fact = |x| {...} yazdığında ve oraya bir tane atıfta bulunamaz.

Bu Rust 1,17 çalışır, ancak bazı durumlarda tehlikeli beri the blog post The Case of the Recurring Closure özetlendiği gibi muhtemelen gelecekte yasadışı yapılabilir. Burada hiçbir mutasyon olmadığı için burada tamamen güvenli. Bir şeye yakalamak için gerekmiyorsa

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

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

Bu çalışıyor:


Bir başka seçenek sadece, aynı zamanda bir fonksiyonu inline tanımlanabilir bir fn öğe gibi bir özyinelemeli fonksiyon yazmaktır çevreden


Bir seçenek daha fn madde çözümü kullanabilirsiniz ancak açıkça istediğiniz args/çevreyi geçmektir.

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

Bunların hepsi Rust 1.17 ile çalışır ve muhtemelen sürüm 0.6'dan beri çalışmışlardır. fn 'un fn s içinde tanımlanmış olması, üst düzeyde tanımlanmış olanlardan farklı değildir, sadece içeride tanımlanmış oldukları fn içinde erişilebilir olmaları dışında.