我想从序言中的现有列表中创建一个过滤列表

I want to make a filtered list out of a existing list in prolog

问题是;我有一个混合元素列表 [Y, donkey, horse, 30, father(max)]。我想从中创建一个新列表,只包含原子。

我想写一个名为 atoms/2 的 Prolog 谓词,当在第一个参数位置提供一个列表时,将实例化在第二个参数位置提供的变量,其中包含出现在输入列表(包括任何可能的重复元素)。

像这样:

?- atoms([Y, donkey, horse, 30, father(max)], Result).
Result = [donkey, horse].
  1. 安装 SWI-Prolog。
  2. 开始吧。
  3. ?- 提示符下键入:edit(include/3). 并按 Enter。
  4. 当您看到 include/3 的库定义时惊叹不已。
  5. 将其用作谓词的起点。
  6. 思考开源的重要性。

或者,查看此答案:

由于大多数 Prolog 系统实现了 member/2 列表成员谓词以及标准 atom/1findall/3 谓词,一个简单的可移植解决方案是:

atoms(List, Atoms) :-
    findall(
        Atom,
        (member(Atom, List), atom(Atom)),
        Atoms
    ).

调用示例:

| ?- atoms([Y, donkey, horse, 30, father(max)], Atoms).

Atoms = [donkey,horse]

yes

我假设这是为了完成作业,并且您的讲师希望您编写自己的代码,而不是使用内置的 member/2findall/3

Prolog 有一个内置谓词 atom/1,我们将使用它来识别原子。

而序言是一种递归语言。诀窍是学会递归思考。大多数递归问题可以分解为 1 或 2 个特殊情况,然后是更一般的递归情况。

所以...

我们有一个特例,空列表:

atoms( [] , [] ).

我们有两个一般的递归案例。一种是列表的头部(第一项)是一个原子的情况,这种情况我们将列表项添加到结果列表中并向下递归:

atoms( [X|Xs], [X|Atoms] ) :- atom(X), !, atoms(Xs,Atoms) .

other/last情况是当前列表项不是原子的情况,在这种情况下我们简单地丢弃列表项并向下递归。

atoms( [X|Xs],    Atoms  ) := \+atom(X), atoms(Xs,Atoms) .

把它们放在一起,你得到

atoms( []     , []        ) .
atoms( [X|Xs] , [X|Atoms] ) :-    atom(X), atoms(Xs,Atoms) .
atoms( [X|Xs] ,    Atoms  ) :- \+ atom(X), atoms(Xs,Atoms) .

请注意,这可以简化以消除冗余的原子性测试。将列表项作为原子的标识是确定性,也就是说,列表项不会不再是原子,我们再看一遍,所以我们可以使用 cut ( !) 运算符修剪搜索树:

atoms( []     , []        ) .
atoms( [X|Xs] , [X|Atoms] ) :-    atom(X), !, atoms(Xs,Atoms) .
atoms( [X|Xs] ,    Atoms  ) :-                atoms(Xs,Atoms) .