如何找到对目标没有贡献的事实

时间:2013-05-28 07:32:05

标签: prolog code-coverage failure-slice

我试图编写一个可以检查学生计划是否能达到某个目标的程序。我可以做那个部分。现在,我想检查学生程序是否实际包含不必要的代码。为了解决这个问题,我想我需要知道学生计划是否包含对指定目标没有贡献的事实。但是,我无法弄明白,如何找到对目标没有贡献的事实。

为了便于理解,让我们考虑一个更简单的例子。在这个例子中,指定的目标是:约翰是tomy的祖父吗?

father(john, jim).
father(jim, tomy).
father(john, david).
father(bruce, anne).
mother(mary, jim).

grandfather(A,B) :- father(A, X), father(X,B).

goal:- grandfather(john, tomy).

实际上,目标只能通过以下事实来满足:

father(john, jim).
father(jim, tomy).

我想知道的事情是哪些事实实际上对目标没有贡献。答案将是以下所有事实:

father(john, david).
father(bruce, anne).
mother(mary, jim).

非常感谢任何帮助。 感谢

3 个答案:

答案 0 :(得分:2)

您的问题无法在Prolog中直接回答,但您可以使用failure-slice手动回答。只需将false目标添加到您的程序中,并始终测试goal是否仍然成功。这是我获得的最小程序。

father(john, jim).
father(jim, tomy).
father(john, david) :-  false.
father(bruce, anne) :- false.
mother(mary, jim) :- false.

grandfather(A,B) :- father(A, X), father(X,B).

goal:- grandfather(john, tomy).

每次将目标false插入到纯粹的单调程序中时,您都确信该解决方案集合已减少(或保持不变)。因此,找到这样的切片涉及尽可能多的试验,因为有些地方可以设定这样的目标。有时您可能希望添加目标X = term以进一步缩小计划范围。

当您想要了解程序的终止属性时,故障片特别有用,有关详细信息,请参阅

答案 1 :(得分:1)

令人难以置信的是,这个问题有部分解决方案here。要在这里重现相关部分是实质性的代码,所以让我明确指出这不是我自己的工作,我只是将这里的工作包括在内,以防将来上面的网站丢失。< / p>

首先,您需要一个元循环解释器:

mi_circ(true).
mi_circ((A,B)) :-
    mi_circ(A),
    mi_circ(B).
mi_circ(clause(A,B)) :-
    clause(A,B).
mi_circ(A \= B) :-
    A \= B.
mi_circ(G) :-
    G \= true,
    G \= (_,_),
    G \= (_\=_),
    G \= clause(_,_),
    clause(G, Body),
    mi_circ(Body).

适用于\=/2clause/2。要将此模式推广到所有内置谓词,我们可以使用predicate_property / 2来识别它们,以便直接调用它们:

provable(true, _) :- !.
provable((G1,G2), Defs) :- !,
    provable(G1, Defs),
    provable(G2, Defs).
provable(BI, _) :-
    predicate_property(BI, built_in),
    !,
    call(BI).
provable(Goal, Defs) :-
    member(Def, Defs),
    copy_term(Def, Goal-Body),
    provable(Body, Defs).

这为您提供了一个具体化的元解释器,这意味着您可以传递provable/2一个目标和一组定义,它将告诉您提供的定义是否足以证明目标。我打赌你现在可以品尝我们与最终解决方案的距离!

通过以下附加定义,我们可以使用此MI来识别某些谓词定义中的冗余事实​​:

redundant(Functor/Arity, Reds) :-
    functor(Term, Functor, Arity),
    findall(Term-Body, clause(Term, Body), Defs),
    setof(Red, Defs^redundant_(Defs, Red), Reds).

redundant_(Defs, Fact) :-
    select(Fact-true, Defs, Rest),
    once(provable(Fact, Rest)).

这是使用select/3一次分出一个定义,看看谓词是否仍可证明。通过在所有定义中执行此操作,您可以获得所有不必要规则的集合。

鉴于定义:

as([]).
as([a]).   % redundant
as([a,a]). % redundant
as([A|As]) :-
    A = a,           % test built-in =/2
    5 is 2 + 3,      % test built-in is/2
    1 > 0,           % test built-in >/2
    as(As).

我们可以要求从所有(各自)剩余条款中推导出的事实,因此是多余的:

?- redundant(as/1, Reds).
Reds = [as([a]), as([a, a])]

唉,这对你的问题没有开箱即用的功能,但我认为通过一些研究你可以找到一种方法将这种技术应用到它并提出一些东西。例如,您可以创建一个元解释器,它使用事实列表来检查并执行相同类型的remove-one-then-prove循环来查找它们。

希望这有帮助,至少是有趣的。

答案 2 :(得分:0)

另一个选择是修改和使用执行谓词子句覆盖的单元测试框架。定义一个单元测试,其中包含您想要找出哪些子句不对其做出贡献的目标。如有必要,修改位将修改覆盖率报告以识别这些子句。就像我的意思一样,如果不清楚,请使用Logtalk发行版中的一个示例考虑Logtalk lgtunit工具的以下输出:

?- {ack(tester)}.
% 
% tests started at 2013/6/5, 19:54:9
% running tests from object tests
% file: /Users/pmoura/logtalk/examples/ack/tests.lgt
% ack_1: success
% ack_2: success
% ack_3: success
% 3 tests: 0 skipped, 3 passed, 0 failed
% completed tests from object tests
% ack: ack/3 - [1,2,3] (3/3)
% 1 unit declared in the test file containing 3 clauses
% 1 unit covered containing 3 clauses
% 3 out of 3 clauses covered, 100,000000% coverage
% tests ended at 2013/6/5, 19:54:9
% 
true.

该行:

% ack: ack/3 - [1,2,3] (3/3)

显示ack/3谓词的三个单元测试使用了哪些子句。