Prolog:"findall"用于有限数量的解决方案

Ser*_*nko 9 prolog

假设我想找到谓词的所有解的总和,我可以使用

findall(L, find(L), Sols),
Run Code Online (Sandbox Code Playgroud)

并且只是总结了Sols的成员.

但是如果find(L)有很多(无限的,可能)解决方案,我只想得到它们中的前10个呢?

我希望它可用于B-Prolog和ECLiPSe CLP.

fal*_*lse 7

有许多类似的用途,因此可以考虑在两者之间定义一些抽象.例如call_firstn(Goal_0,N),最多可以获得第一批N许多答案Goal_0.这反过来可以使用call_nth(Goal_0, Nth).

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 ).
Run Code Online (Sandbox Code Playgroud)

完全实现call_nth/2不泄漏但仍然是可重入的,不能直接在ISO Prolog中定义.你需要求助于各种低级操作,这些操作通常是不完整的语义,而这些语义对于常规程序员来说是更好的隐藏.


jsc*_*mpf 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)
        )
    ).
Run Code Online (Sandbox Code Playgroud)

这是可重入的并且不会泄漏内存(两者都是全局变量或基于动态谓词的解决方案的问题).如果您希望它能够正确处理模块,则需要少量添加.

当然,您可以使用与Paulo使用的assert/retract原语相同的代码结构.