我在一个更大的代码库中遇到了这个问题,但将其简化为一个最小的可重复示例。这是汇编程序的一些代码:
:- 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所展示的那样。这可能有效(然后很好(,但理解起来更复杂。而且在某些情况下可能效率较低。这是一个真正的工程问题。在某些情况下,我们什么时候接受不一致性作为更快解决方案的代价?