我想创建一个谓词 divisors(X,[Y]),如果 X>1 并且 Y 是 X 的所有除数的列表,从 X 开始一直到 1,则该谓词为 true。
我的代码现在的样子:
divisors(1,[1]).
divisors(X,[Y,Z|Ys]) :-
X>0,
Y is X,
Y>Z,
divides(X,[Z|Ys]).
divides(X,[Y,Z|Ys]) :-
Y>Z,
0 is X mod Y,
divides(X,[Z|Ys]).
divides(X,[1]).
Run Code Online (Sandbox Code Playgroud)
但它有几个问题:
如果询问列表(例如?-divisors(10,X)),prolog 将返回错误。
?- 除数(X,[Y])。其中 [Y] 是不完整的除数列表,则为 true...
盖伊·编码器编辑
这个答案是由 OP 提出的,并发布在下面的评论中。
搬到这里以便其他人可以看到它。
divisors(X,R) :-
X > 1,
divisors(X,1,[],R).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0).
divides(N,D) :-
0 is N mod D.
Run Code Online (Sandbox Code Playgroud)
Op 还指出了这个版本中的一些错误:
如果我询问像 (10,[1,2,3]) 这样的错误语句,它不会终止。
如果我询问类似 (X, [10,5,2,1]) 的语句,它会抛出错误。(-> 参数未充分初始化。)
虽然威廉的答案很好而且可能更快,但这里的答案更接近您所写的内容。
divides(N,D) :-
0 is N mod D.
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0) :-
\+ divides(N,D).
divisors(_,0,R,R).
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 - 1,
divisors(N,D,R1,R).
divisors(X,R) :-
X > 1,
divisors(X,X,[],R), !.
Run Code Online (Sandbox Code Playgroud)
例子:
?- between(1,15,N), divisors(N,Rs).
N = 2,
Rs = [1, 2] ;
N = 3,
Rs = [1, 3] ;
N = 4,
Rs = [1, 2, 4] ;
N = 5,
Rs = [1, 5] ;
N = 6,
Rs = [1, 2, 3, 6] ;
N = 7,
Rs = [1, 7] ;
N = 8,
Rs = [1, 2, 4, 8] ;
N = 9,
Rs = [1, 3, 9] ;
N = 10,
Rs = [1, 2, 5, 10] ;
N = 11,
Rs = [1, 11] ;
N = 12,
Rs = [1, 2, 3, 4, 6, 12] ;
N = 13,
Rs = [1, 13] ;
N = 14,
Rs = [1, 2, 7, 14] ;
N = 15,
Rs = [1, 3, 5, 15].
Run Code Online (Sandbox Code Playgroud)
编辑
OP修改了他们的代码,查看有问题的更新并有一些错误。
此版本解决了这些错误。
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(_,_,R0,R0).
divides(N,D) :-
0 is N mod D.
Run Code Online (Sandbox Code Playgroud)
第一个错误:It doesn't terminate if I ask a wrong statement like divisors(10,[1,2,3]).
通过添加除数/2 来固定
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
Run Code Online (Sandbox Code Playgroud)
和
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
Run Code Online (Sandbox Code Playgroud)
它只处理分母列表而不是生成列表。
第二个错误:It throws an error if I ask a statement like divisors(X, [10,5,2,1]). (-> Arguments are not sufficiently initialized.)
通过进一步添加来解决divisor/2
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
Run Code Online (Sandbox Code Playgroud)
它检查第一个参数是否X是变量,如果是则返回false。另一种选择是生成无限的答案列表。虽然可能没有要求。