同时应用谓词来过滤列表(SWI Prolog)

时间:2018-02-28 18:24:27

标签: concurrency prolog swi-prolog

我的问题:应用谓词过滤列表并行

我有一个列表,我有一个谓词。在实践中,它是一个很长的列表,谓词需要一段时间。我想只输出满足谓词的列表元素。

我可以访问大量的CPU内核,所以我认为尝试并行测试列表中的元素是有意义的。

我试过的东西:

[concurrent_] maplist无效

似乎应该很容易。 concurrent_maplist似乎是maplist的简单并发版本。

根据another answer on this sitemaplist/3应该完全符合我的要求。 maplist/3对于SWI的:- use_module(library(apply)). f(A):- (A*A < 10), writeln(A). set(0,[]). set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T). 建议可能它的行为与答案中描述的不同,但在评论中,答案的作者表明这是文档及其中的问题应该按预期工作。

它似乎不适合我。

我对此进行了如下测试:

?- set(4,L), concurrent_maplist(f, L, O).
ERROR: Undefined procedure: f/2
ERROR:   However, there are definitions for:
ERROR:         f/1

这不起作用:

maplist

与普通set(4,L), maplist(f, L, O). ERROR: Undefined procedure: f/2 ERROR: However, there are definitions for: ERROR: f/1 相同的问题:

include

include有效,但并非

做什么做我想要的事情(不是并行)?- set(11,L), include(f, L, O). 1 2 3 L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...], O = [1, 2, 3] .

concurrent

set(0,[]):-!. set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T). f(A, B):- (A*A < B), writeln(A). par_test(A, B):- set(A, S), findall(f(Good, B), (member(Good, S)), Goals), concurrent(8, Goals, []). [编辑]运行!但它是平行的吗?

我一直试图使用The docs部分遵循concurrent/3的例子来使其发挥作用。

加载此文件:

?- set(4, S), findall(f(Good, 4), member(Good, S), Goals), concurrent(8, Goals, []).
1
false.

...

htop

但是看f(使用更长时间运行的谓词代替concurrent_maplist),它似乎只运行一个核心: - (。

如何并行执行此操作?

有没有办法通过include,或/blog的类似简单并行版本,或其他实现我想要的方式来实现这一目标?

2 个答案:

答案 0 :(得分:1)

让我们测试concurrent_maplist:

test_concurrent_maplist(C) :-
    numlist(1,C,Ns),
    concurrent_maplist(prime,Ns,Ps),
    sumlist(Ps,N),
    format('we have ~d primes in first ~d natural numbers~n', [N,C]).
test_maplist(C) :-
    numlist(1,C,Ns),
    maplist(prime,Ns,Ps),
    sumlist(Ps,N),
    format('we have ~d primes in first ~d natural numbers~n', [N,C]).

prime(N,1) :-
    prime(N), !.
prime(_,0).

% from https://en.wikipedia.org/wiki/Primality_test
prime(N) :- N =< 1, !, fail.
prime(N) :- N =< 3, !.
prime(N) :- (0 is N mod 2; 0 is N mod 3), !, fail.
prime(N) :- prime_(N,5).

prime_(N,I) :-
    I * I =< N, !,
    ( ( 0 is N mod I; 0 is N mod (I + 2) ) -> fail
    ; J is I + 1, prime_(N,J)
    ).
prime_(_,_).

在4核机器上,它产生

?- time(test_concurrent_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 2,100,109 inferences, 1.799 CPU in 2.217 seconds (81% CPU, 1167205 Lips)
true.

?- time(test_concurrent_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 21,000,109 inferences, 18.244 CPU in 36.764 seconds (50% CPU, 1151063 Lips)
true.

?- time(test_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 16,151,437 inferences, 3.942 CPU in 3.951 seconds (100% CPU, 4096903 Lips)
true.

?- time(test_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 402,953,287 inferences, 102.334 CPU in 102.302 seconds (100% CPU, 3937617 Lips)
true.
毫无疑问,这是一项改进:

?- F1 is (2.217/3.951)*100, F2 is (36.764/102.334)*100.

如果列表越长,我们接近1/3的经过时间。

而不是concurrent_maplist / 3,我们可以坚持使用concurrent_maplist / 2并将结果存储在数据库,全局变量等中......

答案 1 :(得分:0)

maplist将第一个参数作为谓词,并将此谓词作为参数应用于作为maplist的参数给出的每个列表中的一个元素。这必然意味着maplist的所有列表参数都具有相同数量的元素。在这种情况下,f接受一个参数,但maplist(f, _, _)期望f接受2个参数。因此错误,未定义的过程:f / 2 ,这意味着Prolog无法找到带有2个参数的f

所以maplist并没有真正做到你想要的。您想要在某些其他语言中使用名为select的内容,其中您只在第二个列表中包含通过应用于第一个元素中的元素的过滤器的元素。

以下是一个示例实现:

select(_, [], []).
select(Filter, [R|Rs], Fs) :-
    (   call(Filter, R)
    ->  Fs = [R|SF]
    ;   Fs = SF
    ),
    select(Filter, Rs, SF).

然后致电select(f, L, O)

如果在您的示例中,您有任何具有该属性的过滤器,它们将在列表中的某个点上成功,那么此后继续失败,您可能希望优化过滤器本身和在第一次失败后没有继续遍历列表。

select_until_fail(_, [], []).
select_until_fail(Filter, [R|Rs], Fs) :-
    (   call(Filter, R)
    ->  Fs = [R|SF],
        select_until_fail(Filter, Rs, SF)
    ;   Fs = []
    ).

或类似的东西(未经测试)。

毕竟,我可能并没有真正回答问题的“并发”部分。