在我的Prolog灵感语言Brachylog中,有可能标记具有潜在无限域的CLP(FD)等效变量。可以找到执行此标注的代码here(感谢Markus Triska @mat)。
此谓词需要存在谓词positive_integer/1
,该行必须具有以下行为:
?- positive_integer(X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
…
这在我们当前的解决方案中实现:
positive_integer(N) :- length([_|_], N).
我可以看到两个问题:
这会很快变慢:
?- time(positive_integer(100000)).
% 5 inferences, 0.000 CPU in 0.001 seconds (0% CPU, Infinite Lips)
?- time(positive_integer(1000000)).
% 5 inferences, 0.000 CPU in 0.008 seconds (0% CPU, Infinite Lips)
?- time(positive_integer(10000000)).
% 5 inferences, 0.062 CPU in 0.075 seconds (83% CPU, 80 Lips)
对于有点过大的数字,这最终会返回Out of global stack
错误:
?- positive_integer(100000000).
ERROR: Out of global stack
这显然是因为Prolog需要实例化列表,如果它的长度很大就很糟糕。
我们怎样才能改进这个谓词,这样即使对于非常大的数字也能有相同的行为?
答案 0 :(得分:5)
已经发布了许多好的想法,并且它们在不同程度上发挥作用。
@vmg有正确的直觉:between/3
与约束不能很好地融合。为了看到这一点,我想使用以下查询作为额外的基准:
?- X #> 10^30, positive_integer(X).
考虑到测试用例,我建议使用以下解决方案:
positive_integer(I) :-
I #> 0,
( var(I) ->
fd_inf(I, Inf),
( I #= Inf
; I #\= Inf,
positive_integer(I)
)
; true
).
关键思想是使用CLP(FD)反射谓词 fd_inf/2
来推断变量域中的最小元素。当您将解决方案移植到其他Prolog系统时,这是您需要更改的唯一谓词。例如,在SICStus Prolog中,谓词称为fd_min/2
。
当然,非常清楚哪一点是最重要的。
creatio ex nihilo :
?- positive_integer(X).
X = 1 ;
X = 2 ;
X = 3 .
已修复整数:
?- X #= 12^42, time(positive_integer(X)).
% 4 inferences, 0.000 CPU in 0.000 seconds (68% CPU, 363636 Lips)
X = 2116471057875484488839167999221661362284396544.
约束整数:
?- X #> 10^30, time(positive_integer(X)).
% 124 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 3647059 Lips)
X = 1000000000000000000000000000001 ;
% 206 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 2367816 Lips)
X = 1000000000000000000000000000002 ;
% 204 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 2428571 Lips)
X = 1000000000000000000000000000003 .
首先,请务必查看Code Golf上的Brachylog和最新Brachylog solutions。感谢Julien的努力,一种受Prolog启发的语言现在越来越多地主持一些最简洁优雅的程序。朱利安很棒的工作!
请不要使用between/3
的特定于实现的异常:这些异常会破坏谓词的重要语义属性,并且无法移植到其他系统。
如果您忽略(2),请使用infinite
代替inf
。在CLP(FD)的上下文中,inf
表示整数集的下限,这是正无穷大的相反。
在CLP(FD)的上下文中,我建议使用CLP(FD)约束代替 between/3
以及不采用约束的其他谓词考虑到。
事实上,我建议使用CLP(FD)约束代替 所有低级谓词来推理整数。这最多可以使您的程序更通用,而不是更具体。
非常感谢您对此问题和发布的解决方案感兴趣!我希望您发现以上测试用例对您的变体有用,并找到在您的版本中考虑CLP(FD)约束的方法,以便它们运行得更快,我们都可以对它们进行投票!
答案 1 :(得分:4)
Since "Brachylog's interpreter is entirely written in Prolog" meaning SWI-Prolog, you can use between/3
with the second argument bound to inf.
Comparing your positive_integer
with
positive_integer_b(X):- between(1,inf,X).
Tests on my machine:
?- time(positive_integer(10000000)).
% 5 inferences, 0.062 CPU in 0.072 seconds (87% CPU, 80 Lips)
true.
9 ?- time(positive_integer_b(10000000)).
% 2 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
true.
And showing "Out of global stack":
13 ?- time(positive_integer(100000000)).
% 5 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
ERROR: Out of global stack
14 ?- time(positive_integer_b(100000000)).
% 2 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
true.
I don't think between is pure prolog though.
答案 2 :(得分:1)
In case you want your code to be runnable also on other systems, consider:
positive_integer(N) :-
( nonvar(N), % strictly not needed, but clearer
integer(N),
N > 0
-> true
; length([_|_], N)
).
This version produces exactly the same errors as your first try.
You have indeed spotted a bit towards a weakness in current length/2
implementations. Ideally, a goal like length([_|_], 1000000000000000)
would take some time, but at least does not consume more than constant memory. On the other hand, I am not too sure if this is worth optimizing. After all, I do not see an easy way to solve the runtime problem for such cases.
Note that the version of between/3
in SWI-Prolog is highly specific to SWI. It makes termination arguments much more complex. In other systems like SICStus, you know for sure that between/3
is terminating, regardless of the arguments. In SWI you would have to prove, that the atom inf
will not be encountered which raises the burden of proof obligation.
答案 3 :(得分:1)
没有/ 3和ISO兼容(我认为)
positive_integer(1).
positive_integer(X) :-
var(X),
positive_integer(Y),
X is Y + 1.
positive_integer(X) :-
integer(X),
X > 0.