同时应用谓词来过滤列表(SWI Prolog)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同时应用谓词来过滤列表(SWI Prolog)相关的知识,希望对你有一定的参考价值。
My problem: apply a predicate to filter a list in parallel
我有一个列表,我有一个谓词。在实践中,它是一个很长的列表,谓词需要一段时间。我想只输出满足谓词的列表元素。
我可以访问大量的CPU内核,所以我认为尝试并行测试列表中的元素是有意义的。
Stuff I've tried:
[concurrent_
] maplist
不起作用
好像它应该很容易。 concurrent_maplist
似乎是maplist
的简单并发版本。
根据another answer on this site,maplist/3
应该完全按照我的意愿行事。 The docs对于SWI的maplist/3
表示可能它的行为与答案中描述的不同,但在评论中,答案的作者认为这是文档的一个问题,它确实应该按预期工作。
它似乎对我不起作用。
我测试了这个如下:
:- 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
[编辑]运行!但它是平行的吗?
我一直试图使用concurrent/3
使用部分遵循another answer的例子。
加载此文件:
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, []).
...
?- set(4, S), findall(f(Good, 4), member(Good, S), Goals), concurrent(8, Goals, []).
1
false.
但是看着htop
(用更长时间运行的谓词代替这个f
),它似乎只运行一个核心:-(。
我该如何并行执行此操作?
有没有办法用concurrent_maplist
,或类似简单的include
并行版本,或其他方式实现我想要的目标?
让我们测试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并将结果存储在数据库,全局变量等中......
maplist
将第一个参数作为谓词,并将此参数作为参数应用于每个列表中作为maplist
参数给出的一个元素。这必然意味着maplist
的所有列表参数都具有相同数量的元素。在这种情况下,f
接受一个参数,但maplist(f, _, _)
希望f
接受2个参数。因此错误,Undefined procedure: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 = []
).
或类似的东西(未经测试)。
毕竟,我可能没有真正回答问题的“并发”部分。
以上是关于同时应用谓词来过滤列表(SWI Prolog)的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在Swi-Prolog中获得以毫秒为单位的经过时间?