【问题标题】:How do I make a new filtered list out of existing list in Prolog?如何从 Prolog 中的现有列表中创建一个新的过滤列表?
【发布时间】:2020-09-05 06:38:52
【问题描述】:

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

问题是; 我有一个混合元素列表 [Y, rat, gorilla, 30, mother(alex)]。我想用原子来制作一个新的列表。

所以查询应该是这样的。

?- atoms([Y, rat, gorilla, 30, mother(alex)], Result).
Result = [rat, gorilla].

我试过了,但我不知道如何解决这个问题。我认为它应该是递归的,因为它需要检查每个项目是否是原子。

atoms([], []).

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

【问题讨论】:

    标签: list recursion prolog


    【解决方案1】:

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

    请注意,这是一种出色的函数式编程方法,这并没有什么问题:“逻辑程序”的大块实际上是用函数式风格编写的。我们开始:

    在 SWI-Prolog 中,过滤的谓词称为 include/3exclude/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.
    

    很好。

    【讨论】:

    • 感谢您回答我的问题。即使对于像我这样的初学者来说,这也是一个非常明确的答案。谢谢!
    • 我同意更喜欢显式测试而不是削减。但我不同意盲目削减。 atoms_i 的第三个子句中的删减没有任何作用:一旦进入谓词的最后一个子句,就别无选择了!
    • @IsabelleNewbie 但是我为什么要考虑剪辑是否有任何作用?剪辑是对下一个出现并阅读源代码的人的信号:我想承诺这个选择。省略它意味着读者必须进入上下文:“这真的是最后一个子句吗?”。在任何情况下,Prolog 都应该是保护部件的专用符号,就像 CHR 一样。如果有合理的理由不添加“!”,让我们将其留给编译器警告来提出问题。
    • 把它留在里面意味着读者必须想知道为什么首先会有剪辑。从我们正在进行讨论的事实可以看出。你显然不同意,这很好。无论如何,在这种情况下,我会使用( atom(H) -> ... ; ... ),它的所有优点是在重要的地方进行一次切割,而不是复制警卫,并明确表示我们是在“语义”条件下进行分支(而不是“句法”分支,由于不同的子句头)。
    • @IsabelleNewbie 这是一个有趣的想法。是的,我喜欢这样。
    猜你喜欢
    • 2015-04-19
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多