編集者注: この回答は Rust 1.0 より前のバージョンのものであり、Rust 1.0 では構文的に有効でないコードが含まれています。
を定義する方法だけが必要な場合は、apply
Rust の例によるマクロの構文拡張を試してください。
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
それが呼び出されるファイルに表示される必要があります。