SWI Prolog报告比特移位CLPFD的错误答案



我在一个更大的代码库中遇到了这个问题,但将其简化为一个最小的可重复示例。这是汇编程序的一些代码:

:- use_module(library(clpfd)).
bigconst(X) :- X #=< 0x3FF, X #>= 0.
asm(instruction('ADD', [A]), R) :-
bigconst(A),
R #= 0b0000 + (A << 4).
asm(instruction('SUB', [A]), R) :-
bigconst(A),
R #= 0b0001 + (A << 4).

组装时似乎有效:

?- asm(instruction('SUB', [5]), R).
R = 81.

但在拆卸时似乎失败了:

?- asm(I, 81).
I = instruction('ADD', [_42146]),
_42146 in 0..1023,
81#=_42146<<4 .

这是我程序中的错误还是Prolog中的错误?我该怎么解决这个问题?

当我找到答案时,它是LOL。我使用了许多奇怪的模式来解决问题,但这是我以前从未使用过的模式。一旦我看到它的工作,我就知道我有了一个新的工具箱工具。

对于CLP(FD(问题,它们通常可以双向工作,并且是您想要的。您遇到的第一个问题是您有bigconst(A),它的行为类似于guard语句。所以把它扔掉吧。

接下来的事情是R #= 0b0000 + (A << 4)按预期工作,但遇到了一个问题,它在两个方面都没有按预期工作

?- X #= 4 << 4.
X = 64.
?- 64 #= X << 4.
64#=X<<4.

同样,反向

B #= A >> 4.

也如预期的那样工作,并且也遭受同样的问题。

?- X #= 64 >> 4.
X = 4.
?- 4 #= X >> 4.
4#=X>>4.

所以我尝试使用in/2添加一些约束,但没有成功,然后意识到我已经拥有了所需的所有约束,它们也起作用了。

asm(instruction('ADD', [A]), R) :-
R #= 0b0000 + (A << 4),
A #= (R - 0b0000) >> 4.

示例用法

?- asm(instruction('ADD', [5]), R).
R = 80.
?- asm(I,80).
I = instruction('ADD', [5]).

为了表明这不是一个昙花一现的奇迹。

asm(instruction('SUB', [A]), R) :-
R #= 0b0001 + (A << 4),
A #= (R - 0b0001) >> 4.

示例用法

?- asm(instruction('SUB', [5]), R).
R = 81.
?- asm(I,81).
I = instruction('SUB', [5]).

这是我的程序中的错误还是Prolog中的错误?我该怎么解决这个问题?

这是您正在使用的顶层的可用性错误。如果你仔细观察,你可能会发现一个小小的提示:

81#=_42146<<4 .
^ SPACE

这个微小的空间意味着:请注意,答案不止于此。事实上,有两个答案。第一个答案(来自'ADD'(是假的,它不包含任何解决方案。只有第二个答案包含一个解决方案。

请注意,Prolog主要提供答案,而不是解决方案。这就是我们谈论答案替换的原因。答案和解决方案之间的关系是微妙的。一个答案可能包含0到无穷多个解。

那么Prolog为什么不直接生产解决方案呢?

首先,对于无限的解决方案,这将是非常无效的。想想length(L,3),它有一个单一的答案,包含所有长度为3的列表,其中只有任何可以想象的元素,因此有无限多的解。如L = [1,2,3]L = [bof, toto, machin-maschin]L = [louis_XVI, bernie, bernie]等。借助逻辑变量,我们可以将这三个元素列表的无穷大分解为以下答案:L = [_A,_B,_C]。这就是逻辑变量的力量!

这种能力也用于约束。但有了这种权力,就有了很多责任和负担。毕竟,许多问题在一个方向上很容易计算,而在另一个方向则很难计算。你发现了这样一个问题。在这种情况下,可以考虑改进clpfd来轻松处理这种情况,但在一般情况下,这是不可决定的。因此,有时没有任何算法。在这种情况下,给出一个假答案(不一致,Scheinlösung,solution blanche(是系统能做的最好的事情。或者,它可能会产生错误并中止整个计算。但这太恶心了。通常,我们可以忍受这种不一致。

以你的情况为例,如果你真的想确保解决方案的存在,那么通过标记它们来消除答案中的限制!

?- asm(I,81), I = instruction(_,[R]).
I = instruction('ADD', [R]),
R in 0..1023,
81#=R<<4
;  I = instruction('SUB', [R]),
R in 0..1023,
80#=R<<4.
?- asm(I,81), I = instruction(_,[R]), labeling([],[R]).
I = instruction('SUB', [5]),
R = 5
;  false.

另一种方法是增强约束,正如@Guy Coder所展示的那样。这可能有效(然后很好(,但理解起来更复杂。而且在某些情况下可能效率较低。这是一个真正的工程问题。在某些情况下,我们什么时候接受不一致性作为更快解决方案的代价?

最新更新