1

我想计算没有任何参数的谓词的成功和失败次数。为简单起见,我要测试的谓词是 test_arith/0。test_arith/0 对 is/2 进行了 3 个测试(再次,为了简单起见。我真的很想测试我正在编写的更复杂的谓词,但这些细节会分散这个问题的注意力。)(PS 我看到了另一个关于计算谓词为真的次数。我想一次性计算成功和失败。我不想在每个测试用例中多次运行真正的谓词,因为其中一些需要很长时间执行。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 似乎只被调用一次。我应该得到

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。我需要失败计数,并且每个谓词在每次测试运行时只运行一次,在这种情况下需要 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

我认为问题在于 (->)/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 回答
0

看起来你的 findall 中有一个错字,你的暗示的“else”将 F 绑定到 0 和 1,而不是 P 到 0 和 F 到 1。这是直接从你的代码中复制的吗?

如果是这样,那可以解释为什么聚合方法只接受真值;虚假案件永远不会过去。

编辑添加:

虽然我认为利用 findall 之类的功能是一种很好的做法,但有时你无法击败一个好的故障循环;内存使用率相当低,我发现性能相似。在 Sicstus prolog 中,我的方法几乎肯定是这样的:

## 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 回答