2012-02-14 9 views
5

Rust'a QuickCheck'u gönderiyorum ve for_all hariç her şeyi yazdım. Tip imzasının ne olması gerektiğinden emin değilim, genel olarak, for_all'un bir lambda özelliği ve bir jeneratör lambda koleksiyonunu kabul edeceğini biliyorum. for_all, mülkiyeti girdi olarak vermek için rastgele bir test vakası oluşturmak amacıyla jeneratörler değerlendirecektir. for_all, özellik true değerini döndürürse +++ OK, passed 100 tests. yazmalıdır. Aksi halde, *** Failed! yazmalı ve hatalı test durumu değerlerini yazdırmalıdır.Rust'ta Lisp (başvuru) veya (köri) nasıl öykünebilirim?

cevap

0

Tam olarak ne istediğinizi tarif edebilir misiniz? Ben ne için soruyorsun böyle bir şey olduğunu düşünüyorum: İhtiyacınız olan tüm apply tanımlamak için bir yol ise

+0

Oldukça değil. Bunu açıklamakta zorlanıyorum. http://www.yellosoft.us/quickcheck bunu daha iyi yapar. Eğer Lisp'in '(uygula)' Rüstünde yeniden üretebiliyorsanız, gerisini yapabilirim. – mcandre

2

, Rust makro-by-örneğin sözdizimi uzantıları deneyin: Yukarıdaki

fn main() { 
    #macro[[#apply[f, [x, ...]], f(x, ...)]]; 

    fn add(a: int, b: int) -> int { a + b } 

    assert (#apply[add, [1, 15]] == 16); 
} 

kod the Rust test suite'dan alınmıştır.

Ne yazık ki, sözdizimi uzantıları ile ilgili belgeler şu anda biraz seyrek. Rust reference manual muhtemelen en iyi bahistir - bunun vermiş olduğu örnek (apply, daha az değil) eskimiş olsa da, ne kadar bilgisinin güvenilir olduğuna emin değilim.

Güncelleme:

Hepsi ... keyfi jeneratörler kabul sağ tip imzasıyla bir işlevde iddia eklemek sarmak için nasıl bulmaktan Geriye kalan.

Hala bu arada koyarak konum tam olarak emin değilim, ama burada bir int üreten herhangi bir işlev kabul eden bir fonksiyon var: Pas olduğu sayı ile çalışan, ancak

use std; 
import std::rand; 

fn assert_even(num_gen: fn() -> int) -> (bool, int) { 
    let num = num_gen(); 
    ret (num % 2 == 0, num); 
} 

fn main() { 
    let rng = rand::mk_rng(); 

    let gen_even = {|| (rng.next() as int) * 2}; 

    log(error, assert_even(gen_even)); 
} 

use std; 
import std::rand; 

iface is_even { fn is_even() -> bool; } 

impl of is_even for int { fn is_even() -> bool { self % 2 == 0 } } 

impl of is_even for u32 { fn is_even() -> bool { self % 2u == 0u } } 

fn assert_even<T: is_even>(num_gen: fn() -> T) -> (bool, T) { 
    let num = num_gen(); 
    ret (num.is_even(), num); 
} 

fn main() { 
    let rng = rand::mk_rng(); 

    let gen_even_int = {|| (rng.next() as int) * 2}; 
    let gen_even_u32 = {|| rng.next() * 2u}; 

    log(error, assert_even(gen_even_int)); 
    log(error, assert_even(gen_even_u32)); 
} 
: herhangi bir sayısal türe assert_even genelleme istiyorsanız arayüzleri/uygulamaları tanımlamak ve sonra sınırlı bir cins assert_even beyan gerekecek ve şu anda bir ağrı tür

Yan not: Test etmekle ilgileniyorsanız, Rust'un tiptate olanaklarını kontrol etmelisiniz. Hangi türün yaptığını ve programın doğruluğunu gerçekleştirme yöntemlerini gösteren bir örnek için bkz. the Rust manual here. Anladığım kadarıyla, temelde Eiffel'in design by contract'un daha güçlü bir versiyonu.

Güncelleme 2:

for_all tek bir özellik (örn is_even veya divisible_by) ve jeneratör işlevlerine bir koleksiyon kabul eder. Jeneratörler, eşyaya girdi olarak geçmek için rastgele değerler döndüren lambdaşlardır, örn. div_you için [gen_int] is_even veya [gen_int, gen_int]. for_all, bir test vakası olarak oluşturulan değerleri kullanarak özelliği çağırır, +++ OK yazdırır, 100 rastgele test vakası için true değerini döndürürse 100 testi geçti veya * Başarısız! Test durumlarından biri başarısız olursa {test_case}.

Bu tam kaynak dosya tamamen umarım, aradığınız davranışı göstermesi gerekmektedir (for_all tanımı çok alt yakındır):

use std; 
import std::rand; 
import std::io::println; 

iface is_even { fn is_even() -> bool; } 

impl of is_even for int { fn is_even() -> bool { self % 2 == 0 } } 

fn main() { 
    let rng = rand::mk_rng(); 

            // Cast to int here because u32 is lame 
    let gen_even = {|| (rng.next() as int) * 2}; 
    let gen_float = {|| rng.next_float()}; 

    // Accepts generators that produce types that implement the is_even iface 
    fn assert_even<T: is_even>(num_gen: fn() -> T) -> bool { 
     let num = num_gen(); 
     let prop_holds = num.is_even(); 
     if !prop_holds { 
      println(#fmt("Failure: %? is not even", num)); 
     } 
     ret prop_holds; 
    } 

    fn assert_divisible(num_gen1: fn() -> float, 
         num_gen2: fn() -> float) -> bool { 
     let dividend = num_gen1(), 
      divisor = num_gen2(); 
     let prop_holds = dividend/divisor == 0f; 
     if !prop_holds { 
      println(#fmt("Failure: %? are not divisible", (dividend, divisor))); 
     } 
     ret prop_holds; 
    } 

             // Begin anonymous closure here 
    #macro[[#for_all[prop, [gen, ...]], {|| 
     let passed_tests = 0; 
     let prop_holds = true; 
     // Nice iterators and break/continue are still being implemented, 
     // so this loop is a bit crude. 
     while passed_tests < 100 && prop_holds { 
      prop_holds = prop(gen, ...); 
      if prop_holds { passed_tests += 1; } 
     } 
     println(#fmt("Tests passed: %d", passed_tests)); 
     ret 0; // Necessary to infer type of #for_all, might be a compiler bug 
    }()]]; // Close anonymous closure and self-execute, then close #macro 

    #for_all[assert_even, [gen_even]]; 
    #for_all[assert_divisible, [gen_float, gen_float]]; 
} 

Bir şey daha: sözdizimi uzatma mekanizması hala oldukça parlatılmamış, bu yüzden farklı kasalardan makroları ithal etmek mümkün değil. O zamana kadar, #for_all'un tanımı, çağrıldığı dosyada görünmek zorunda kalacaktır.

+0

bu çok yardımcı oluyor! Geriye kalan tek şey, keyfi jeneratörü kabul eden doğru tip imzalı bir işleve 'add ... assert' nasıl sarılacağını anlamaktır. '1' ve' 15' yerine, rasgele sayılar (veya herhangi bir tür) üreten işlevler. – mcandre

+0

@mcandre Geçtiğini beklediğiniz işlevlerin imzaları ile birlikte bir 'for_all' çağrısı örneği verebilir misiniz? –

+0

https://wiki.call-cc.org/eggref/4/cluckcheck adresini ziyaret edin ve "herkes için" ifadesini bulun veya kaydırın. – mcandre

6

Pas, tüm işlevler sabit sayıda parametre alır, bu nedenle genel durumda Lisp apply'a eşdeğer bir şey yoktur. Fakat makrolar size arzu ettiğiniz soyutlamayı sağlayabilir. Yeni, parlak ve şu anda tamamen belgesiz makro sistemde, yazabilirsiniz:

Sonra

macro_rules! for_all { 
    { $tester:expr, $($generator:expr),* } 
    => { $tester($($generator()),*) } 
} 

(. Gerçekten yakında () yerine makrolarında {} arasında kullanmaya başlamak umuyoruz unutmayın), for_all!{ |a:int,b:int| { a+b }, || 4, || 7 }, 11 üretir.

eski makro sistemde

, eşdeğerdir: Projenizle

#macro[[#for_all[tester, generator, ...], tester((generator()), ...)]] 

İyi şanslar!

+1

Ve makrolar hala düzgün bir şekilde alınamıyor, ancak bunu uygulayana kadar yapabileceğiniz kirli bir korsanlık var: 'include! {"/Path/to/file.rs "}' kullanın ve makro tanımlarını kıvırcık kaşlı bir blok içinde çevrili dosya. Yine de, 'include!' Ifadesini kullanmanı söylediğim kişilere söyleme. (: –

+0

Müthiş kodunuz için teşekkür ederiz! İhtiyacım olan son şey 1) yoludur. 1) oluşturulan değerleri geçici olarak bir çeşit koleksiyonda saklar 2) test fonksiyonunu her bir test örneğine koleksiyondaki 3) uygularsanız başarısız, yazdır "FAIL!"ve bu test durumundaki değerleri yazdırın 4) 1-3 adımlarını test edin, böylece 100 test vakası test cihazına karşı çalıştırılır, herhangi bir test durumu başarısız olursa durdurulur. 100 test vakasından hiç biri başarısız olmazsa," Ok "u yazdırın Obi Wan Kanobi, sen benim tek umudumsun. – mcandre