控制Prolog变量值选择

时间:2017-01-12 19:30:19

标签: prolog evaluation

an earlier question的启发,我试图实现一些能够枚举布尔表达式的可能性。但是,我在变量选择方面遇到了麻烦。这是我的预期结果:

?- eval(X^Y, R).
R = 0^0;
R = 0^1;
R = 1^0;
R = 1^1;
no.

这是我的代码:

:- op(200, yfx, ^).

split(V, R) :- var(V), R = 0.
split(V, R) :- var(V), R = 1.
split(X ^ Y, XP ^ YP) :- split(X, XP), split(Y, YP).

即使对于这个简单的案例,这已经做不到我想做的事了:

?- split(Y, R).
R = 0 ;
R = 1 ;
Y = _G269^_G270,
R = 0^0 ;
Y = _G269^_G270,
R = 0^1 ;
Y = _G269^ (_G275^_G276),
R = 0^ (0^0) ;
Y = _G269^ (_G275^_G276),
R = 0^ (0^1) ;
Y = _G269^ (_G275^ (_G281^_G282)),
R = 0^ (0^ (0^0)) .

所以,我可以看到问题是什么,这是通过split(Y, YP)的道路上Prolog已经用尽前两个条款,所以它再次在split(X^Y, ...)结束,统一我的{{ 1}}与Y,基本上。我只是不确定我需要做什么来关闭那条路径,除非我在结构X'^Y'处开始。

我也喜欢这样使用嵌套结构,所以我不能消除分支的递归处理。

修改:不使用运算符

如果^/2困扰你,请考虑以下表述:

op/3

这就是那种情况下的代码:

eval(and(X,Y), R).
R = and(0,0);
R = and(0,1);
R = and(1,0);
R = and(1,1);
no.

请记住,我仍然喜欢使用split(V, R) :- var(V), R = 0. split(V, R) :- var(V), R = 1. split(and(X,Y), and(XP,YP)) :- split(X, XP), split(Y, YP). 之类的递归公式。

3 个答案:

答案 0 :(得分:6)

主要问题:默认表示

这种情况下的核心问题是您用来表示布尔表达式的默认表示

“默认”是指您无法通过模式匹配清楚地区分案例:在您的情况下,变量V可能表示

  • 其中一个命题常量01
  • A^B形式的复合表达。

由于这个缺点,你不能干净地表达“变量X仅代表程序中两个命题常量之一”形式的约束。

出路:清洁代表

声明性的出路是使用清洁表示 而不是

例如,假设我们任意使用v/1来区分仅表示命题常量的变量,那么我们就有:

  • v(X)表示命题变量X
  • A^B表示复合表达式

显然,由于主要的仿函数和arities不同(v/1(^)/2),我们可以通过模式匹配来区分这些情况。

使用此表示形式,您的代码段变为:

split(v(V), V) :- V = 0.
split(v(V), V) :- V = 1.
split(X^Y, XP ^ YP) :-
        split(X, XP),
        split(Y, YP).

示例查询:

?- split(v(X)^v(Y), R).
X = Y, Y = 0,
R = 0^0 ;
X = 0,
Y = 1,
R = 0^1 ;
X = 1,
Y = 0,
R = 1^0 ;
X = Y, Y = 1,
R = 1^1.

请注意,此仍然所有方向中工作,同样在最常见的情况下

?- split(Expr, R).
Expr = v(0),
R = 0 ;
Expr = v(1),
R = 1 ;
Expr = v(0)^v(0),
R = 0^0 ;
Expr = v(0)^v(1),
R = 0^1 ;
etc.

根据经验,一旦你必须在你的代码中使用像var/1这样的逻辑外谓词,就没有希望保持其逻辑纯度和单调性。瞄准干净的表示以保留这些属性。

有时,使用默认表示是不可避免的,例如,因为您希望使用户更容易输入。在这种情况下,目标是在开始实际推理之前快速将它们转换为干净的。

答案 1 :(得分:3)

在某些时候,我写了一个非常小的程序,似乎或多或少是你想要的。我真的希望我不会完全误读你的问题。您可以查看整个计划的this SWISH program。存在一些差异,例如使用ft代替0和1,或使用andor代替^和{{1} }。但是,基本的想法和你的一样(我想)。

如果我删除一些不必要的东西用于演示,那么这就是程序:

v

这里的要点是我有一个纯粹的关系,我的:- op(100, fy, ?). :- op(500, yfx, and). :- op(500, yfx, or). table(F, T) :- term_variables(F, Vs), bagof(R-Vs, bl(F, R), T). bl(?X, R) :- v(X, R). bl(X and Y, R) :- bl(X, R0), and_bl(R0, Y, R). bl(X or Y, R) :- bl(X, R0), or_bl(R0, Y, R). v(f, f). v(t, t). and_bl(f, _, f). and_bl(t, Y, R) :- bl(Y, R). or_bl(f, Y, R) :- bl(Y, R). or_bl(t, _, t). ,它只是说明v/2的值是truetrue的值是false。然后,我使用一个相互递归的定义来实际评估布尔表达式,最终在false达到最低点。

我决定自己“标记”布尔变量,因此您需要编写v/2而不是X。如果我没记错的话,当时我意识到我需要一种方法来明确说明我是否在表达式中有一个布尔变量,或者整个表达式是否仍然是“未知”。换句话说,?X可以是?Xt,而f可以是Expr?X或{{not ?X 1}},或?X and ?Y,或?X or ?Y,依此类推。

使用not (?X and ?Y)标记变量是避免“默认表示”为what @mat explained的原因。重要的是,如果没有明确标记,我看不到从布尔表达式中区分布尔变量的方法。

以下是如何使用它:

?X

答案 2 :(得分:3)

已经有两个很好的答案,一个是纯粹的答案,另一个是使用term_variables/2的短答案。我还想谈一个细节:

  

所以,我可以看到问题是什么,这是通过split(Y, YP)的道路上Prolog已经用尽前两个条款,所以它再次在split(X^Y, ...)结束,统一我的{{ 1}}与Y,基本上。我只是不确定我需要做什么来关闭那条路径,除非我在结构X'^Y'处开始。

如果您不希望使用^/2执行头部统一,如果第一个参数是变量,请将统一从头部拉出来。没有什么能迫使你把术语X^Y放在那里!你想要做的是只考虑前两个条款不适用的最后一个条款。前两个条款由X^Y保护,因此您可以使用var/1保护最后一个:

nonvar/1

通过这个,您的示例按预期工作:

split(V, R) :- var(V), R = 0.
split(V, R) :- var(V), R = 1.
split(Term, SplitTerm) :-
    nonvar(Term),
    Term = X ^ Y,
    split(X, XP),
    split(Y, YP),
    SplitTerm = XP ^ YP.

编辑:正如评论中指出的那样,使用?- split(Y, R). R = 0 ; R = 1 ; false. ?- split(X^Y, R). R = 0^0 ; R = 0^1 ; R = 1^0 ; R = 1^1 ; false. 之类的测试会让您遇到各种各样的麻烦。

一个问题是你必须仔细检查各种级别的实例化:mat var/1的示例查询失败并带有上述代码(它应该成功split(1, R)) 。这是因为R = 1属于1案例,但未与nonvar统一。如果我们采用这种方式,我们必须区分_^_var个案,而不是nonvarvarground - 但并非 - nonvar。这有点乱。

另一个问题是查询ground,它在声明上等同于上面的查询,但成功两次,包括解决方案split(V, R), V = 1。这是因为R = V的这个定义(故意)避免在其论点之间引入共享。我们是否希望共享与否取决于规范。

我不会尝试使用实例化测试来提供一个完全可靠的解决方案,因为我的观点并不是这是最好的方法。