提问人:Joseph Garvin 提问时间:2/7/2023 最后编辑:falseJoseph Garvin 更新时间:2/9/2023 访问量:215
在序言中表达 collatz 猜想的不同方式失败了
Different ways of expressing collatz conjecture in prolog fail
问:
我正在使用 SWI Prolog 和此处的教程学习 prolog。我发现,如果我像他们在视频中所做的那样表达 collatz 猜想,只要我替换我猜想是 vs 差异,它就会起作用。但是,如果我调整定义,它似乎会破裂,要么是错误的,要么是错误的结论。为什么我的替代定义失败?法典:#=
is
swipl
scryer-prolog
use_module(library(clpfd)).
%% Does work, collatz_next(A, 1) gives A=2
collatz_next(N0, N) :-
N0 is 2*N.
collatz_next(N0, N) :-
N0 is 2*_ + 1,
N is 3*N0 + 1.
%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),((N0 / 2) is N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).
%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),(N0 is 2*N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).
%% Doesn't work
%% "Arguments are not sufficiently instantiated"
%% collatz_next(N0, N) :-
%% N0 / 2 is N.
%% collatz_next(N0, N) :-
%% N0 is 2*_ + 1,
%% N is 3*N0 + 1.
答:
正如 brebs 已经在评论中所写的那样,如果您使用的是 SWI-Prolog,则必须包含该行。但是,如果您使用的是 Scryer Prolog,则该库称为 clpz,因此您必须包含 .如果您使用各自的库,则谓词(来自链接的视频)适用于视频中描述的两个 Prolog(我使用 Scryer Prolog 版本 0.9.0. 和 SWI-Prolog 版本 8.4.2,均为 Linux 机器上的 64 位)对其进行了测试)。由于评论中尚未提及,我还想向您推荐 The Power of Prolog 中对 CLP(FD) 和 CLP(Z) 的描述。:- use_module(library(clpfd)).
:- use_module(library(clpz)).
collatz_next/2
现在回答你关于失败的替代方案的问题。如果右侧的表达式计算结果为左侧的数字,则内置值为 true。如果左侧是未实例化的变量,则该变量包含调用目标后右侧表达式的值。为了成功,需要对右侧表达式中的所有变量进行状态化。请看以下示例:is/2
?- 3 is 2+1. true. ?- X is 2+1. X = 3. ?- 3 is X+1. ERROR: Arguments are not sufficiently instantiated ... ?- 3 is 2+X. ERROR: Arguments are not sufficiently instantiated ...
此外,如果左侧使用浮点数而不是整数实例化,则将失败:is/2
?- 3.0 is 2+1. false.
现在我们已经介绍了一些基础知识,让我们来看看你的第一个谓词:
%% Does work, collatz_next(A, 1) gives A=2 collatz_next(N0, N) :- N0 is 2*N. collatz_next(N0, N) :- N0 is 2*_ + 1, N is 3*N0 + 1.
让我们观察一下,虽然你给出的示例产生了一个正确的答案,但在按下 -键后,它会抛出一个实例化错误:;
?- collatz_next(A, 1). A = 2 ; ERROR: Arguments are not sufficiently instantiated ...
这是怎么回事?您正在发布查询,因此谓词 head 中的变量与变量统一,变量与 统一。有了这些统一,第一条规则中的唯一目标 ,现在变成了 。这就产生了答案。Prolog 现在尝试第二条规则,其中第一个目标现在变为 。在这里,右侧仍然包含一个变量 (),因此表达式没有充分的状态来计算,因此 Prolog 抛出实例化错误。collatz_next(A, 1).
N0
A
N
1
N0 is 2*N
A is 2*1
A = 2
collatz_next/2
N0 is 2*_ + 1
A is 2*_ + 1
_
现在让我们尝试反过来使用谓词。正如您在 Youtube 视频中看到的那样,如果那么预期的答案是 .但是,如果您使用谓词进行查询,则不会得到答案和实例化错误:N0=5
N=16
?- collatz_next(5, N). ERROR: Arguments are not sufficiently instantiated ...
查看您的定义,我们可以观察到规则头部的变量与 统一,而第二个参数仍未实例化。因此,第一条规则中的单个目标 ,由于右侧的变量而成为实例化误差。collatz_next/2
N0
5
N
N0 is 2*N
5 is 2*N
请注意,该视频还显示,由于使用了 CLP(FD)/CLP(Z),最一般的查询仍在生成答案,而使用 的版本再次产生实例化错误。:- collatz_next(N0,N).
is/2
您发布的接下来的两个版本(带有评论的版本)失败,规则中的第一个目标。由于你查询的规则头部中的变量与变量是统一的,所以所有四个规则中的第一个目标分别变为 和 。如果尝试将这些目标作为查询,答案是错误的:collatz_next/2
%% Doesn't work, collatz_next(A, 1) gives false
:- collatz_next(A, 1).
N0
A
((A mod 2) is 0)
((A mod 2) is 1)
?- ((A mod 2) is 0). false. ?- ((A mod 2) is 1). false.
由于规则的第一个目标失败了,Prolog 甚至不会尝试第二个目标,因为一旦你在(链)连词中有一个,它就不能产生真。两个谓词的两个规则都是这种情况,因此您的查询的答案是错误的。另一方面,如果您尝试将左手边换成右手边,您将收到一个实例化错误:false
is/2
?- (0 is (A mod 2)). ERROR: Arguments are not sufficiently instantiated ... ?- (1 is (A mod 2)). ERROR: Arguments are not sufficiently instantiated ...
也许可以这样想:如果你试图计算一个未实例化的变量模一个实际数字,你会期望会发生什么?一个合理的期望是得到某种反馈,说明这是无法做到的。
另一个合理的观点是期望 Prolog 将发布的目标作为约束进行传播,直到它(希望)在以后得到解决。这基本上就是 CLP(FD)/CLP(Z) 所做的(另请参阅 The Power of Prolog 中关于 CLP(FD) 和 CLP(Z) 中的约束传播部分。只需尝试上述查询:(#=)/2
?- ((A mod 2) #= 0). A mod 2#=0. % residual goal ?- ((A mod 2) #= 1). A mod 2#=1. % residual goal ?- (0 #= (A mod 2)). A mod 2#=0. % residual goal ?- (1 #= (A mod 2)). A mod 2#=1. % residual goal
正如你所看到的,已发布的约束现在被传播,并在扣除结束时显示为剩余目标,因为在这些情况下,查询仅由这些单个目标组成,因此无法进一步解决它们。
正如 TessellatingHeckler 在评论中指出的那样,您发布的最后一个版本(标有评论)在第一条规则的单个目标中具有错误的左侧和右侧。但是,即使您交换它们,除非实例化变量,否则您也会收到实例化错误。但即便如此,一旦 Prolog 尝试第二条规则,您仍然会收到实例化错误,因为它的第一个目标包含一个始终未实例化的变量:%% Doesn't work
is/2
N0
N0 is 2*_ + 1
_
?- N0 is 2*_ + 1. ERROR: Arguments are not sufficiently instantiated ... ?- 1 is 2*_ + 1. ERROR: Arguments are not sufficiently instantiated ...
底线是:如果你想使用低级谓词,你必须意识到它们的局限性。如果你想对整数进行声明式推理,你就不能轻易地绕过 CLP(FD)/CLP(Z)。如果您决定使用视频中所示的谓词,则无法将 CLP(FD)/CLP(Z) 约束一对一地交换为低级谓词,例如并期望获得相同的结果。is/2
collatz_next/2
is/2
评论
library(clpz)
在 SICStus 上运行,然后是后来的 Scryer。
评论
:- use_module(library(clpfd)).
N0 / 2 is N
“是”不是这样工作的;答案在左边,算术术语在右边,只有。:-