List 如何从Prolog中的现有列表中生成新的筛选列表?

List 如何从Prolog中的现有列表中生成新的筛选列表?,list,recursion,prolog,List,Recursion,Prolog,我是Prolog的新手,我已经需要帮助了。我查了其他类似的问题,但没有回答我的问题 问题是,; 我有一个混合元素的列表[Y,老鼠,大猩猩,30岁,母亲(亚历克斯)]。我想用原子做一个新的列表 所以查询应该是这样的 ?- atoms([Y, rat, gorilla, 30, mother(alex)], Result). Result = [rat, gorilla]. 我试过了,但我不知道如何解决这个问题。我认为它应该是递归的,因为它需要检查每个项目是否是原子 atoms([], []).

我是Prolog的新手,我已经需要帮助了。我查了其他类似的问题,但没有回答我的问题

问题是,; 我有一个混合元素的列表[Y,老鼠,大猩猩,30岁,母亲(亚历克斯)]。我想用原子做一个新的列表

所以查询应该是这样的

?- atoms([Y, rat, gorilla, 30, mother(alex)], Result).
Result = [rat, gorilla].
我试过了,但我不知道如何解决这个问题。我认为它应该是递归的,因为它需要检查每个项目是否是原子

atoms([], []).

atoms([H | T], Result) :-
        atom(H),
        append(H, [], Result).

您要做的是所谓的“过滤”,并且已经有了一个现成的“高级谓词”。为什么是“更高层次”?因为它不只是处理一阶“对象”,而是采用它调用的可执行目标

请注意,这是一种非常实用的编程方法,这并没有什么错:一个“逻辑程序”的大块实际上是用函数式编写的。我们开始:

在SWI Prolog中,过滤的谓词称为or

还有一点单元测试代码:

:- begin_tests(filtering).

test("basic test", true(Result = [rat, gorilla])) :-
   atoms([Y, rat, gorilla, 30, mother(alex)], Result).

:- end_tests(filtering).
:- 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 . done
% test passed
true.
?- run_tests.
% PL-Unit: filtering_i . done
% test passed
true.
它起作用了

当然,您始终可以使用递归调用编写自己的
atoms/2
(也称为使用归纳定义)

人们会说你可以省去原子(H)。虽然他们是对的,但我觉得这样做非常烦人,因为我更喜欢源代码中的对称性和原则上可以一时兴起删除的剪切。另外,现在是时候让编译器开始做一些工作来发现效率本身了。现在是2020年,不是1980年

让我们添加一些单元测试代码:

:- begin_tests(filtering).

test("basic test", true(Result = [rat, gorilla])) :-
   atoms([Y, rat, gorilla, 30, mother(alex)], Result).

:- end_tests(filtering).
:- 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 . done
% test passed
true.
?- run_tests.
% PL-Unit: filtering_i . done
% test passed
true.

很好。

谢谢你回答我的问题。即使对于像我这样的初学者来说,这也是一个非常清楚的答案。非常感谢。我同意更喜欢显式测试而不是切割。但我不同意盲目地削减开支。
atoms\u i
的第三个子句中的cut没有任何作用:一旦进入谓词的最后一个子句,就没有选择了@伊莎贝尔,菜鸟,但我为什么要考虑一个伤口是否有用呢?切割是对下一个过来阅读来源的人的一个信号:我想做出这个选择。省略它意味着读者必须进入上下文:“这真的是最后一句吗?”。Prolog在任何情况下都应该是防护部件的专用符号,如CHR。如果有合理理由不添加“!”,让我们把它留给编译器警告来提出这个问题。把它放在一边意味着读者必须想一想,为什么首先会出现剪切。你可以从我们正在进行的讨论中看出这一点。你显然不同意,没关系。无论如何,在这种情况下,我将使用
(atom(H)->…;…)
,它的所有优点是在重要的地方有一个切口,不重复保护,并明确表示我们是在“语义”条件上分支(与由于不同的子句头而导致的“语法”分支相反).@IsabelleNewbie这是个有趣的主意。是的,我喜欢。