所以我对Prolog来说相对比较新,虽然这个问题在很多其他语言中很容易,但我遇到了很多麻烦。我想为数字N生成一个因子列表。我已经构建了一个谓词,告诉我数字是否是一个因素:
% A divides B
% A is a factor of B
divides(A,B) :- A =\= 0, (B mod A) =:= 0.
% special case where 1 // 2 would be 0
factors(1,[1]) :- !.
% general case
factors(N,L):- N > 0, factor_list(1, N, L).
factor_list(S,E,L) :- S =< E // 2, f_list(S,E,L).
f_list(S,E,[]) :- S > E // 2, !.
f_list(S,E,[S|T]) :- divides(S,E), !, S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
任何帮助将不胜感激。
修改
我几乎改变了我的整个解决方案,但出于某种原因,像factors(9, [1])
这样的谓词返回true,当我只希望factors(9, [1,3])
返回true时。有什么想法吗?
答案 0 :(得分:1)
这就是为什么factors(9,[1])
为真:尝试实例化的时间(也就是说,统一)是关闭的:
f_list(S,E,[]) :- S > E // 2, !.
f_list(S,E,[S|T]) :- divides(S,E), !, S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
%% flist(1,9,[1]) -> (2nd clause) divides(1,9), S1 is 2, f_list(2,9,[]).
%% flist(2,9,[]) -> (3rd clause) S1 is 3, f_list(3,9,[]).
%% ...
%% flist(5,9,[]) -> (1st clause) 5 > 9 // 2, !.
因为您预先指定[1]
,当它达到3时尾部为[]
并且与第2个子句的匹配被阻止,尽管将成功到期到divides/2
。
解决方案是将统一从条款的头部移到身体中,并且只在适当的时间制作它们,而不是更早:
f_list(S,E,L) :- S > E // 2, !, L=[].
f_list(S,E,L) :- divides(S,E), !, L=[S|T], S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
以上通常用if-else结构编写:
f_list(S,E,L) :-
( S > E // 2 -> L=[]
; divides(S,E) -> L=[S|T], S1 is S+1, f_list(S1, E, T)
; S1 is S+1, f_list(S1, E, L)
).
您还可以将主谓词简化为
%% is not defined for N =< 0
factors(N,L):-
( N =:= 1 -> L=[1]
; N >= 2 -> f_list(1,N,L)
).
答案 1 :(得分:1)
就个人而言,我使用了一个更简单的解决方案:
factors(1,[1]):- true, !.
factors(X,[Factor1|T]):- X > 0,
between(2,X,Factor1),
NewX is X // Factor1, (X mod Factor1) =:= 0,
factors(NewX,T), !.
这个只接受有序的因素列表。
答案 2 :(得分:1)
这是一个简单的基于枚举的过程。
factors(M, [1 | L]):- factors(M, 2, L).
factors(M, X, L):-
residue(M, X, M1),
((M==M1, L=L1); (M1 < M, L=[X|L1])),
((M1=1, L1=[]); (M1 > X, X1 is X+1, factors(M1, X1, L1))).
residue(M, X, M1):-
((M < X, M1=M);
(M >=X, MX is M mod X,
(MX=0, MM is M/X, residue(MM, X, M1);
MX > 0, M1=M))).