在Prolog元解释器中将证明作为输出参数

时间:2019-04-02 20:59:11

标签: prolog metaprogramming interpreter inference

我正在组装一个简单的元解释器,该解释器输出证明的步骤。我在将证明步骤作为输出参数时遇到麻烦。我的谓词explain1以我想要的详细形式返回证明,但没有作为输出参数返回。我的谓词explain2将证明作为输出参数返回,但没有我想要的详细程度。可以修改explain2使其产生与explain1一样多的信息吗?我不需要它来输出文本“ Explaining ...”和“ Explanation ...”,而只需输出实际的explanans和explanandum。

程序底部的玩具数据(“如果健康,富足,那么快乐”)只是一个示例,其想法是建立一个包含有关其他事物的更多事实的数据库。我想尝试做一个接受效果的谓词,例如happy(john),并返回解释。因此,应该由用户输入E的{​​{1}}自变量;因此,另一个查询可能是explain,依此类推。在解释中,我无法直接从explain(_, smokes(mary), _)C变量中获得所需的内容,因为我希望程序输出证明过程中的步骤,其中E和{{1} }有所不同,例如“富有而健康,那么快乐;赢得如此丰富;真正如此富有;真正如此幸福”等等。即返回所有导致结果的因果链接。

马库斯·特里斯卡(Markus Triska)出色的site对此有一些细节,但是我很难适应该代码。

任何帮助将不胜感激!

谢谢/ JCR

我的程序:

C

main1的输出:

E

main2的输出:

main1:-explain1(_, happy(john), _), fail.
main2:-explain2(_, happy(john), _, T), writeln(T), fail.

explain1(C, E, P):-
    C = ['True'],
    p(C, E, P),
    write('Explaining '), write(E), 
    write('. An explanation is: '), write(C), 
    write(' with probability '), write(P), nl.
explain1(C, E, P):-
    p(C, E, P),
    not(C = ['True']),
    write('Explaining '), write(E), 
    write('. An explanation is: '), write(C), 
    write(' with probability '), write(P), nl.
explain1(C, E, P):-
    p(C0, E, P0),
    maplist(explain1, C1, C0, P1),
    flatten(C1, C),
    append([P0], P1, P2),
    flatten(P2, P3),
    foldl(multiply, P3, 1, P),
    write('Explaining '), write(E), 
    write('. An explanation is: '), write(C), 
    write(' with probability '), write(P), nl.

explain2(C, E, P, T):-
    C = ['True'],
    p(C, E, P),
    T = [C, E, P].
explain2(C, E, P, T):-
    p(C, E, P),
    not(C = ['True']),
    T = [C, E, P].
explain2(C, E, P, T):-
    p(C0, E, P0),
    maplist(explain2, C1, C0, P1, _),
    flatten(C1, C),
    append([P0], P1, P2),
    flatten(P2, P3),
    foldl(multiply, P3, 1, P),
    T = [C, E, P].

multiply(V1, V2, R) :- R is V1 * V2.

p(['True'], wins(john), 0.7).
p([wins(john)], rich(john), 0.3).
p(['True'], healthy(john), 0.9).
p([rich(john), healthy(john)], happy(john), 0.6).

1 个答案:

答案 0 :(得分:2)

我不清楚此元解释器的概率部分,但实际上我认为这与您的问题有关,因此,我将尝试勾勒出如何处理此问题。

您可以将call/1视为Prolog的原型解释器,因为它只是证明了一个目标。因此,似乎所需的API类似于prove(+Goal, -Proof),其中的Target就像使用call/1一样被证明,但是您又得到了第二点证明,某种形式的证明。

当正常的Prolog看到类似Goal1, Goal2的表达式时,您可能会想到它会扩展为call(Goal1), call(Goal2)。那么,在这种情况下,您的返回证据的元解释器会做什么?它应该证明两个目标,然后以某种方式组合这些“子证明”。

所有这些向我暗示,您的构想中缺少的是,证明的结构是什么?我会很难考虑要返回哪种类型,因为如果您不想要字符串,那么您会希望可以更轻松地遍历。它可能最终将具有类似于Prolog的树结构(除非没有故障分支)。因此,我希望它具有某种嵌套,并且肯定会以某种方式“类似于”调用堆栈,尽管我希望这会限制您的实用程序(对于通用查询,您将如何有效地遍历该树?)。

让我们考虑一下您的基本情况。可能是这样的:

prove(true, true) :- !.

True本质上是正确的,因为它是正确的。

我感兴趣的下一个案例是“和”。

prove((G1, G2), (P1, P2)) :- 
   !,
   prove(G1, P1), 
   prove(G2, P2).

这看起来像是重言式,但关键思想实际上是我们将G1和G2的证明与证明中的(P1, P2)结合在一起。

下一个情况可能是“或”:

prove((G1;_), P1) :- prove(G1, P1).
prove((_;G2), P2) :- !, prove(G2, P2).

这是我们丢失失败分支的部分。如果第一个分支成功,则其证明将出现在结果中;如果第二个分支成功,则其证明将出现在结果中。但是它们不会出现在结果中。

最后,我们必须按照a question I asked some time ago处理内置函数和用户谓词:

prove(H, subproof(H, Subproof)) :- clause(H, Body), prove(Body, Subproof).
prove(H, builtin(H)) :- call(H).

这时,我们有了一个产生非常简单证明的元解释器。我将添加一些子句,然后使用我们的元解释器进行尝试:

mortal(X) :- man(X).
man(socrates).

以下是查询:

?- prove((member(X, [bread,socrates]), mortal(X)), Proof).
X = socrates,
Proof =  (builtin(member(socrates, [bread, socrates])), 
          subproof(mortal(socrates), 
                   subproof(man(socrates), true))) 

由于我尚不了解的原因,在第二个查询中使用member/2会引起轰炸。我有opened a question about that on the SWI forum,当我知道那里发生了什么时,将更新此答案。

更新。该问题与library(lists)的自动加载有关,这种自动加载在您使用member/2时发生。在第一次调用时,member/2没有子句,因此它输入call/1,后者将调用自动加载器,然后将其作为内置调用。在随后的尝试中,member/2有子句,但是它们的主体在lists模块中包含谓词,并且此元解释器无法正确处理模块。一种快捷的解决方案是将第三个子句更改为此:

prove(H, subproof(H, Subproof)) :- 
   \+ predicate_property(H, imported_from(_)), 
   clause(H, Body), 
   prove(Body, Subproof).

我希望这会有所帮助!