7

QuickCheckをRustに移植for_allしていますが、型アノテーションがどうあるべきかわからない場合を除いて、すべてを記述しました。

for_all一般に、プロパティラムダとジェネレータラムダのコレクションを受け入れることを私は知っています。プロパティを入力として提供するランダムなテストケースを作成するために、ジェネレーターを評価します。

+++ OK, passed 100 tests.プロパティがtrueを返す場合は出力する必要があり、そうでない場合は、*** Failed!問題のあるテストケース値を出力して出力する必要があります。

4

3 に答える 3

7

Rust では、すべての関数が一定数のパラメーターを受け取るためapply、一般的なケースでは Lisp に相当するものはありませんが、マクロを使用すると、必要な抽象化を提供できます。あなたは書ける:

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

次に、for_all!(|a, b| a + b, || 4, || 7)を生成し11ます。

あなたのプロジェクトで頑張ってください!

于 2012-08-06T20:04:42.953 に答える
2

編集者注: この回答は Rust 1.0 より前のバージョンのものであり、Rust 1.0 では構文的に有効でないコードが含まれています。

を定義する方法だけが必要な場合は、applyRust の例によるマクロの構文拡張を試してください。

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

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

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

上記のコードはRust テスト スイートからのものです。

悲しいことに、構文拡張に関するドキュメントは、現時点では少しまばらです。Rust のリファレンス マニュアルはおそらく最善の策ですが、そこに示されている例は (少なくともapply!) 古くなっているため、その情報がどれだけ信頼できるかはわかりません。

アップデート:

あとは、任意のジェネレーターを受け入れる適切な型シグネチャを持つ関数で add ... assert をラップする方法を理解するだけです。

これをどのようにまとめているのかはまだ正確にはわかりませんが、以下を生成する関数を受け入れる関数を次に示しますint

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

ただし、Rust で数値を扱うのは現時点ではちょっと面倒です。任意の数値型に一般化する場合は、インターフェイス/実装を定義してから、制限付きジェネリック型でassert_even宣言する必要があります。assert_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));
}

補足: テストに興味がある場合は、Rust の typestate 機能を確認してください。typestate が行うことの例と、プログラムの正確性を強制する方法については、Rust のマニュアルを参照してください。私の理解では、基本的には契約によるエッフェルのデザインのより強力なバージョンです。

更新 2:

for_all は、単一のプロパティ (is_even または divisible_by など) とジェネレータ関数のコレクションを受け入れます。ジェネレーターは、プロパティへの入力として渡すランダム値を返すラムダです。たとえば、is_even の場合は [gen_int]、divisible_by の場合は [gen_int, gen_int] です。for_all は、生成された値をテスト ケースとして使用してプロパティを呼び出し、+++ OK を出力し、プロパティが 100 個のランダムなテスト ケースに対して true を返す場合は 100 個のテストに合格、または *** 失敗しました! {test_case} テスト ケースの 1 つが失敗した場合。

この完全なソース ファイルは、探している動作を完全に示しているはずです (の定義はfor_all一番下にあります)。

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

もう 1 つ: 構文拡張メカニズムはまだかなり洗練されていないため、異なるクレートからマクロをインポートすることはできません。それまでは、 の定義は、#for_allそれが呼び出されるファイルに表示される必要があります。

于 2012-02-15T15:39:58.857 に答える
0

欲しいものを正確に説明できますか?あなたが求めているのは次のようなものだと思います:

fn for_all<A>(test: fn(A) -> bool, generators: &[fn() -> A]) -> bool {
    generators.iter().all(|gen| test(gen()))
}

fn main() {
    let generators: Vec<fn() -> (i32, i32)> = vec![
        || (1, 2),
        || (2, 3),
        || (3, 4),
    ];

    for_all(|(a, b)| a < b, &generators);
}
于 2012-02-14T18:22:39.207 に答える