1

引数を持たない述語の成功と失敗の数をカウントしたい。簡単にするために、テストする述語は test_arith/0 です。test_arith/0 には is/2 の 3 つのテストがあります (ここでも簡単にするためです。私が書いているより複雑な述語を本当にテストしたいのですが、それらの詳細はこの質問の邪魔になるでしょう。) (PS についての他の質問を見ました述語が真である回数をカウントする. 1 回のパスで成功と失敗の両方をカウントしたい. テスト ケースごとに実際の述語を複数回実行する必要はありません. 一部の述語には時間がかかるためです. aggregate/3 と aggregate_all も同様にひたむきで、成功したものだけを取り上げているようです。)

test_arith(Passes, Failures) :-
    findall(P-F, (test_arith->(P=1,F=0);(P=0,F=1)), Scores),
    summarize_scores(Scores, 0, 0, Passes, Failures).

test_arith :- 5 is 3 +2.  % Test #1: Should pass
test_arith :- 5 is 2 +2.  % Test #2: Should fail
test_arith :- 4 is 2 +2.  % Test #3: Should pass

summarize_scores([], Passes, Failures, Passes, Failures).
summarize_scores([P-F|Scores], Passes_SF, Failures_SF, Passes, Failures) :-
    Next_Passes is P + Passes_SF,
    Next_Failures is F + Failures_SF,
    summarize_scores(Scores, Next_Passes, Next_Failures, Passes, Failures).

私が走るとき

test_arith(P,F).

私は得る

P = 1,
F = 0.

test_arith は 1 回だけ呼び出されるように見えるためです。私は取得する必要があります

P = 2,
F = 1.

ご協力いただきありがとうございます。

私は試した:

test_arith(Passes, Failures) :-
    bagof(P-F, A^(test_arith(A)->(P=1,F=0);(P=0,F=1)), Scores),
    summarize_scores(Scores, 0, 0, Passes, Failures).

test_arith(_) :- 5 is 3 +2.
test_arith(_) :- 5 is 2 +2.
test_arith(_) :- 4 is 2 +2.

test_arith2(Passes) :-
    aggregate(count, A^test_arith(A), Passes).

test_arith(P,F) の結果: P = 1、F = 0. test_arith2(P) の結果は "2" です。(これはうまく機能しますが、私が探しているものの 1/4 しかありません。失敗の数が必要であり、各述語をテスト実行ごとに 1 回だけ実行する必要があります。この場合は 3 回呼び出します。)

次に、テスト ケースごとに番号を追加してみました。

test_arith(Passes, Failures) :-
    bagof(P-F, A^(test_arith(A)->(P=1,F=0);(P=0,F=1)), Scores),
    summarize_scores(Scores, 0, 0, Passes, Failures).

test_arith(1) :- 5 is 3 +2.
test_arith(2) :- 5 is 2 +2.
test_arith(3) :- 4 is 2 +2.

そして得た:

test_arith(P,F).
    P = 1,
    F = 0.
4

2 に答える 2

0

findall にタイプミスがあるようです。意味の「else」は、P を 0 に、F を 1 にではなく、F を 0 と 1 の両方にバインドします。それはコードから直接コピーされていますか?

もしそうなら、それは集計メソッドが真のみを受け入れる理由の説明になる可能性があります。偽のケースは決して通過しません。

追加するために編集:

findall のような関数を利用するのは良い習慣だと思いますが、良いフェイル ループに勝てない場合もあります。メモリ使用量はかなり少なく、パフォーマンスは似ていることがわかりました。Sicstus プロローグでは、私のアプローチはほぼ確実に次のようなものになります。

## function is passed in.
## call(Foo) succeeds.
evaluate(Foo) :-
    call(Foo),
    incrementSuccess,
    !.
## call(Foo) fails.
evaluate(Foo) :-
    incrementFailure.

incrementSuccess :-
    success(N),
    N2 is N + 1,
    retract(success(N)),
    assert(success(N2)),
    !.
incrementSuccess :-
    assert success(1).

[very similar for incrementFailure].

## A fail loop that evaluates all possible bindings
tally(Foo, _Success, _Failure) :-
    evaluate(Foo),
    fail.
## The catch case that passes out the final tallies.
tally(_, Success, Failure) :-
    success(Success),
    failure(Failure).
于 2013-03-18T22:12:43.830 に答える
0

問題は (->)/2 の「暗黙のカット」だと思います。試す

test_arith(Passes, Failures) :-
    findall(P-F, (test_arith, P=1,F=0 ; P=0,F=1), Scores),
    summarize_scores(Scores, 0, 0, Passes, Failures).

そしてあなたは得るでしょう

?- test_arith(P,F).
P = 2,
F = 1.

編集

OT ですが、もちろんライブラリの助けを借りて、ロジックをよりコンパクトにできるのが好きです。ここで同等の定義:

test_arith(Passes, Failures) :-
    findall(R, (test_arith, R=1-0 ; R=0-1), Scores),
    aggregate(r(sum(A),sum(B)), member(A-B, Scores), r(Passes, Failures)).

そして、すぐにスキャンするリストを作成する理由は何ですか?

test_arith(Passes, Failures) :-
    aggregate(r(sum(A),sum(B)), (test_arith, A=1,B=0 ; A=0,B=1), r(Passes, Failures)).

上記のコードを編集すると、失敗を数えることができず、正しくありません。特定のテストケースで動作するように見えたという事実にだまされました。

@false の助けを借りて、ここに reify_call/3 があります。これは、OP の問題を解決できるビルディング ブロックです (SWI-Prolog でテストされています。ここで、 /2は ISO 互換性に関して間違いなく拡張されています。質問に対する @false コメントが与えられています)。

test_arith(Passes, Failures) :-
    aggregate(r(sum(T),sum(F)), reify_call(test_arith, T, F), r(Passes, Failures)).

:- meta_predicate reify_call(0, -, -).

reify_call(Pred, True, False) :-
    clause(Pred, Cl), (call(Cl) -> True = 1, False = 0 ; True = 0, False = 1).
于 2013-03-18T23:08:16.577 に答える