假设我想找到谓词的所有解的总和,我可以使用
findall(L, find(L), Sols),
只是总结Sols的成员。
但是,如果找到(L)有大量(无限可能)解决方案,我只想获得前10个?
我希望这可以在B-Prolog和ECLiPSe CLP中使用。
答案 0 :(得分:7)
有许多类似的用途,因此可以考虑在两者之间定义一些抽象。例如。 call_firstn(Goal_0,N)
N
最多Goal_0
个findfirstn(N, Template, Goal_0, Instances) :-
findall(Template, call_firstn(Goal_0, N), Instances).
call_firstn(Goal_0, N) :-
N + N mod 1 >= 0, % ensures that N >=0 and N is an integer
call_nth(Goal_0, Nth),
( Nth == N -> ! ; true ).
多个答案。这反过来可以使用call_nth(Goal_0, Nth)
实现。
call_nth/2
不会泄漏且仍然是可重入的{{1}}的完整实现无法直接在ISO Prolog中定义。你需要求助于所有类型的低级操作,这些操作通常是不完整的语义,而这些语义对于常规程序员来说是更好的隐藏。
答案 1 :(得分:5)
这是你在ECLiPSe中的表现方式:
find_n(N, Term, Goal, Solutions) :-
( N < 1 ->
Solutions = []
;
record_create(Bag),
shelf_create(count(N), Counter),
(
once((
call(Goal),
recordz(Bag, Term),
\+shelf_dec(Counter, 1) % succeed if enough
)),
fail
;
recorded_list(Bag, Solutions)
)
).
这是可重入的并且不会泄漏内存(两者都是全局变量或基于动态谓词的解决方案的问题)。如果您希望它能够正确处理模块,则需要少量添加。
当然,你可以使用与Paulo使用的断言/收缩原语相同的代码结构。
答案 2 :(得分:3)
ISO Prolog中的便携式解决方案:
:- dynamic(find_n_solution/1).
:- dynamic(find_n_counter/1).
find_n(N, Term, Goal, Solutions) :-
( set_find_n_counter(N),
retractall(find_n_solution(_)),
once((
call(Goal),
assertz(find_n_solution(Term)),
dec_find_n_counter(M),
M =:= 0
)),
fail
; findall(Solution, retract(find_n_solution(Solution)), Solutions)
).
set_find_n_counter(N) :-
retractall(find_n_counter(_)),
assertz(find_n_counter(N)).
dec_find_n_counter(M) :-
retract(find_n_counter(N)),
M is N - 1,
assertz(find_n_counter(M)).
使用样本谓词find/1
@ChristianF回答:
| ?- find_n(10, X, find(X), L).
L = [0,1,2,3,4,5,6,7,8,9]
yes
请注意,即使所需的解决方案数量少于此解决方案,此解决方案仍会返回解决方案列表。一些Prolog编译器,包括B-Prolog和ECLiPSe,提供了可用于实现计数器的非逻辑全局变量,但这会使解决方案变得不可移植。
答案 3 :(得分:3)
SWI-Prolog提供内置谓词findnsols/4和findnsols / 5
gbcRightPane.gridWidth = 5;
rightPanel.add(labelDitigalTimer, gbcRightPanel);
gbcRightPane.gridWidth = 1; // reset for the other components.
您可能希望将整个调用包装成一次/ 1以防止对其他解决方案组进行回溯(例如,如果在上面的示例中您希望1-5列表成为您唯一的解决方案)。
?- findnsols(5, I, between(1, 12, I), L).
L = [1, 2, 3, 4, 5] ;
L = [6, 7, 8, 9, 10] ;
L = [11, 12].
答案 4 :(得分:2)
我刚刚意识到在B-Prolog中通过表格进行操作是微不足道的:
%% an example predicate with infinite solutions
%% from ChristianF answer.
find(0).
find(X) :- find(X1), X is X1+1.
:- table find(-).
?- table_cardinality_limit(find/1, 10), findall(X, find(X), L).
L = [0,1,2,3,4,5,6,7,8,9]
yes
答案 5 :(得分:1)
需要在SWI Prolog中制作一个线程安全的解决方案,我带来了这个:
find_n( N, Solution, Goal, Solutions ) :-
thread_self( Thread_id ),
atomic_list_concat( [counter, Thread_id], Counter_flag_key ),
flag( Counter_flag_key, _, 0 ),
findall( Solution, (
call( Goal ),
flag( Counter_flag_key, X, X ),
X1 is X + 1,
flag( Counter_flag_key, _, X1 ),
( X1 >= N -> !; true )
), Solutions ).
答案 6 :(得分:1)
最近添加的Logtalk线程引擎API或SWI-Prolog协同引擎API提供了另一种解决方案。例如,使用Logtalk线程引擎:
find_at_most(N, Template, Goal, List) :-
threaded_engine_create(Template, Goal, Engine),
collect_at_most(N, Engine, List0),
threaded_engine_destroy(Engine),
List = List0.
collect_at_most(N, Engine, [X| Xs]) :-
N > 0,
threaded_engine_next(Engine, X),
!,
M is N - 1,
collect_at_most(M, Engine, Xs).
collect_at_most(_, _, []).
threaded_engine_create/3
谓词计算第一个解决方案,然后暂停等待threaded_engine_next/2
谓词检索它。当检索到解决方案时,引擎开始计算下一个解决方案,再次暂停直到它被消耗(当在解决方案准备好之前调用threaded_engine_next/2
谓词时,此谓词只是阻止等待它) 。因此,最多计算N
个解。
使用SWI-Prolog协同引擎的解决方案几乎完全相同,但没有阻塞语义(在线程引擎版本中),并且除非通过利用应用程序中的并发(其他地方)来抵消线程的隐式开销,否则预计会更有效:
find_at_most(N, Template, Goal, List) :-
engine_create(Template, Goal, Engine),
collect_at_most(N, Engine, List0),
engine_destroy(Engine),
List = List0.
collect_at_most(N, Engine, [X| Xs]) :-
N > 0,
engine_next(Engine, X),
!,
M is N - 1,
collect_at_most(M, Engine, Xs).
collect_at_most(_, _, []).
两个API还支持_next/2
谓词的已知版本,因此如果您想要消除辅助谓词中的切割,则支持find_at_most/4
谓词的替代实现。
最后,但并非最不重要的是,此处提供的解决方案源自Paul Tarau的工作和发动机论文。
答案 7 :(得分:0)
我认为您需要手动实现findall中隐含的唯一性条件,即您需要聚合解决方案,并且对于每个新解决方案,检查它之前是否尚未被选中。
以下是一个如何运作的示例:
%% an example predicate with infinite solutions
find(0).
find(X) :- find(X1), X is X1+1.
%% myfindall(+Num) holds when there are at least Num different solutions for find
myfindall(Num) :-
length(L, Num),
mfa_aux(L, [], All),
%% now do something with All, e.g., sum it.
writeln(All).
mfa_aux([], All, All).
mfa_aux([H|T], All, Rtv) :-
find(H),
not(member(H, All)), !,
mfa_aux(T, [H|All], Rtv).
%% Test
%% ?- myfindall(10).
答案 8 :(得分:0)
您可以将库解决方案中的findall/3
与limit/2
合并
顺序,从SQL启发:
限制(+计数,:目标)
限制解决方案的数量
https://www.swi-prolog.org/pldoc/man?predicate=limit/2
这是一个示例运行:
?- findall(X, limit(3, (repeat, X = 1)), L).
L = [1, 1, 1].
几个Prolog系统的谓词如limit/2
在他们的曲目中。