用于逻辑编程的良好语言应允许程序员使用与数学家使用的语言接近的语言。因此,我一直认为Prolog中缺少适当的通用量词是一个重要的缺点。
今天,我想到了一种定义比forall
和foreach
更好的东西的想法。
forany(Var, {Context}, Condition, Body)
此谓词试图证明Body
的所有实例Var
都在Condition
上回溯。除非在Condition
或Body
中列出,否则Var
和Context
中的所有变量都被认为是局部变量。 Condition
不允许以任何方式修改Context
中列出的变量,否则forany
将无法正常工作。
这是实现(基于yall):
forany(V, {Vars}, Goal1, Goal2) :-
( bagof(V, {V,Vars}/Goal1, Solutions)
-> maplist({Vars}/[V]>>Goal2, Solutions)
; true ).
我的第一个问题是关于forany
的第二个参数。我想消除它。
构建前8个正方形的列表:
?- length(X,8), forany(N, {X}, between(1,8,N),
(Q is N*N, nth1(N, X, Q))).
X = [1, 4, 9, 16, 25, 36, 49, 64].
撤消列表:
?- X=[1,2,3,4,5], length(X,N), length(Y,N),
forany(I, {X,Y,N}, between(1,N,I),
(J is N-I+1, nth1(I,X,A), nth1(J,Y,A))).
X = [1, 2, 3, 4, 5],
N = 5,
Y = [5, 4, 3, 2, 1].
子集:
subset(X, Y) :- forany(A, {X,Y}, member(A,X), member(A, Y)).
一种有趣的方式来生成列表的所有排列而没有重复项:
permutation(X, Y) :-
length(X, N), length(Y, N), subset(X, Y).
?- permutation([1,2,3],X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
一种对不同整数列表进行排序的有趣方法。注意,约束用于使列表排序,因此不会生成大多数排列:
sorted(X) :- forany(A-B, {X}, append(_, [A,B|_], X),
A#<B).
?- X=[7,3,8,2,6,4,9,5,1], length(X, N), length(Y, N),
sorted(Y), subset(X,Y).
X = [7, 3, 8, 2, 6, 4, 9, 5, 1],
N = 9,
Y = [1, 2, 3, 4, 5, 6, 7, 8, 9] .
在不使用约束的情况下,此forany
表现出色。同样,它可以用来生成约束,但是至少在SWI-Prolog上,已经生成约束时会出现问题。原因是forany
使用bagof
并根据SWI-Prolog的手册:
术语复制操作(
assertz/1
,retract/1
,findall/3
,copy_term/2
等)通常也复制约束。效果从好的,无声的大型约束网络复制到违反约束网络内部一致性的变化不等。根据经验,必须弃用具有属性的复制术语。如果您需要推理约束中涉及的术语,请使用copy_term/3
来获取约束作为Prolog目标,并将这些目标用于进一步处理。
这是bagof
受约束而产生的问题的说明:
?- X=[A,B,C], dif(C,D), bagof(_, K^member(K,X), _).
X = [A, B, C],
dif(C, _5306),
dif(C, _5318),
dif(C, _5330),
dif(C, D).
如您所见,创建了三个不必要的约束。
我的第二个问题是,这是否仅是SWI-Prolog的问题。
第三个问题:是否有办法在SWI-Prolog中解决此问题。手册中的上述引用建议应使用copy_term/3
。不幸的是,我不理解这个建议,也不知道它对forany
是否有用。
答案 0 :(得分:0)
好消息! bagof
是用Prolog编写的,我感到很惊讶。通过查看其代码,我发现我认为不可能的某些事情实际上是可能的。正如SWI-Prolog的手册所建议的那样,copy_term/3
或更确切地说是谓词copy_term_nat/2
有所帮助。
因此,我很高兴能够为SWI-Prolog展示一个完全有效的(据我所知)通用量词:
forany(V, {Vars}, Condition, Body) :-
findall(V-Vars, Condition, Solutions),
% For SWI-Prolog. Can be replaced by Solutions=Clean_solutions in other systems
copy_term_nat(Solutions, Clean_solutions),
forany_execute_goals(Clean_solutions, Vars, V, Body).
forany_execute_goals([], _, _, _).
forany_execute_goals([Sol-NewVars|Solutions], Vars, V, Body) :-
% The following test can be removed
assertion(subsumes_term(NewVars, Vars)),
% or replaced by the following more standard use of throw/1:
% ( subsumes_term(NewVars, Vars)
% -> true
% ; throw('Forbidden instantiation of context variables by the antecedent of forany') ),
NewVars = Vars,
call({Vars}/[V]>>Body, Sol),
forany_execute_goals(Solutions, Vars, V, Body).