灵感来自 an earlier question我试图实现一些可以枚举 bool 表达式可能性的东西。但是,我在变量选择方面遇到了麻烦。这是我的预期结果:
?- 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, ...)
再次将我的 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).
请记住,我仍然希望它能够使用递归公式,例如 and(and(X,Y),and(Y,Z))
等。
最佳答案
主要问题:默认表示
本例中的核心问题是您用来表示 bool 表达式的默认表示。
“默认”是指您无法通过模式匹配清楚地区分情况:在您的情况下,变量 V
可能表示任一个
- 命题常数
0
和1
之一,或 A^B
形式的复合表达式。
由于这个缺点,您无法在程序中清晰地表达“变量 X
仅代表两个命题常数之一”形式的约束。
出路:清洁表示
声明式的出路是使用干净的表示代替。
例如,假设我们任意使用v/1
来区分只表示命题常数的变量,那么我们有:
v(X)
表示命题变量X
A^B
表示一个复合表达式。
显然,由于主仿函数和元数不同(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
这样的逻辑外谓词,就很难保持其逻辑纯度和单调性。以干净的表示为目标以保留这些属性。
有时,使用默认表示是不可避免的,例如,因为您想让用户更轻松地输入。在这种情况下,目标是在开始实际推理之前快速将它们转换为干净的。
关于prolog - 控制 Prolog 变量值选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41621414/