您想要做的就是所谓的“过滤”,并且已经有一个现成的“更高级别的谓词”。为什么是“更高层次”?因为它不仅仅处理一阶“对象”,而是采用它调用的可执行目标。
请注意,这是一种出色的函数式编程方法,这并没有什么问题:“逻辑程序”的大块实际上是用函数式风格编写的。我们开始:
在 SWI-Prolog 中,过滤的谓词称为 include/3 或 exclude/3。
% atoms/2 filters list Li into list Lo using the predicate atom/1
% This only works in direction Li-->Lo.
atoms(Li,Lo) :- include(atom,Li,Lo).
还有一点单元测试代码:
:- begin_tests(filtering).
test("basic test", true(Result = [rat, gorilla])) :-
atoms([Y, rat, gorilla, 30, mother(alex)], Result).
:- end_tests(filtering).
所以:
?- run_tests.
% PL-Unit: filtering . done
% test passed
true.
有效。
当然,您始终可以使用递归调用(也就是使用归纳定义)编写自己的 atoms/2
atoms_i([], []).
atoms_i([H|T], [H|Result]) :- % retain the H in the result list
atom(H), % the "guard" passes if H is atom
!, % then we commit to this branch
atoms_i(T, Result).
atoms_i([H|T], Result) :- % do not retain H in the result list
\+atom(H), % the "guard" passes if H is not atom
!, % then we commit to this branch
atoms_i(T, Result).
人们会说,出于效率原因,您可以在第三个子句中省略\+atom(H),!。虽然他们是对的,但我发现这样做非常烦人,因为我更喜欢源代码中的对称性和原则上可以随意删除的剪切。另外,现在是编译器开始做一些工作以找到效率本身的时候了。现在是 2020 年,而不是 1980 年。
让我们添加一点单元测试代码:
:- begin_tests(filtering_i).
test("basic test", true(Result = [rat, gorilla])) :-
atoms_i([Y, rat, gorilla, 30, mother(alex)], Result).
:- end_tests(filtering_i).
所以:
?- run_tests.
% PL-Unit: filtering_i . done
% test passed
true.
很好。