Prolog DCG:找到最后一个元素

时间:2014-01-30 13:17:45

标签: prolog dcg

我试图更好地理解DCG的使用。为了做到这一点,我尝试将LearnPrologNow书中的一些练习翻译成DCG表示法。但是,我失败了。

我尝试编写一个只列出列表中最后一个元素的程序。就这样。我只是想不出正确的DCG语法来做到这一点。我想我找出应该是的“基本情况”:

  

最后 - > [X | []]。

其中X是最后一个元素。如何让Prolog以递归的方式进入列表?或者我是否以错误的方式思考DCG?

3 个答案:

答案 0 :(得分:6)

... --> [] | [_], ... .

list_last(Xs, X) :-
   phrase((...,[X]), Xs).

这显然是最“图形化”的定义。您可以使用... //0描述很多模式。

语法是描述语言的一种方式。所以你关于如何让Prolog失败的问题是不好的。语法不做任何事情。如果你坚持“生成”句子,他们就会。

对于程序细节,您需要了解终止,但仅限于此。

编辑:如果您真的关心性能,请先测量它。通过SWI,我获得以下内容。请注意使用额外的库来删除phrase/2的调用开销。

?- use_module(library(apply_macros)).
%   library(pairs) compiled into pairs 0.00 sec, 22 clauses
%  library(lists) compiled into lists 0.01 sec, 122 clauses
%  library(occurs) compiled into occurs 0.00 sec, 14 clauses
% library(apply_macros) compiled into apply_macros 0.01 sec, 168 clauses
true.

?- [user].
**omitted**
?- listing.

dcg_last(B, A) :-
        last(A, B, []).

list_last(A, C) :-
        ...(A, B),
        B=[C].

...(A, B) :-
        (   A=B
        ;   A=[_|C],
            ...(C, B)
        ).

last(A, [_|B], C) :-
        last(A, B, C).
last(A, [A|B], B).

:- thread_local thread_message_hook/3.
:- dynamic thread_message_hook/3.
:- volatile thread_message_hook/3.

true.

?- length(L,100000), time(list_last(L,E)).
% 100,000 inferences, 0.018 CPU in 0.030 seconds (60% CPU, 5482960 Lips)
L = [_G351, _G354, _G357, _G360, _G363, _G366, _G369, _G372, _G375|...] ;
% 5 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 294066 Lips)
false.

?- length(L,100000), time(dcg_last(L,E)).
% 100,001 inferences, 0.033 CPU in 0.057 seconds (58% CPU, 3061609 Lips)
L = [_G19, _G22, _G25, _G28, _G31, _G34, _G37, _G40, _G43|...] ;
% 2 inferences, 0.011 CPU in 0.023 seconds (49% CPU, 175 Lips)
false.

因此两者的推理次数大致相同,但dcg_last/2的速度较慢,因为它必须堆积所有无用的选择点。 list_last/2创建相同数量的选择点,但是,它们几乎立即被删除。所以我们有0.018s而0.033s + 0.011s。

答案 1 :(得分:3)

您缺少递归步骤,并使base子句比所需的更复杂。

dcg_last(List, E) :-
    phrase(last(E), List).

last(E) --> [_], last(E).
last(E) --> [E].

last // 1只是跳过任何元素,直到最后。然而,关键是短语/ 2如何翻译作品。 phrase(last(E), List)相当于phrase(last(E), List, []),也就是说,语法必须消耗所有输入。

答案 2 :(得分:1)

这不是答案! CapelliC解释道。只是注释对格式化代码没用,而这个评论属于他的答案:

如果您使用'商家信息'。在咨询之后预测了他的答案,这就是prolog已经重写的内容,并将执行:

last(A, [_|B], C) :-
    last(A, B, C).
last(A, [A|B], B).

dcg_last(B, A) :-
    phrase(last(A), B).

因此DCG只是常规prolog表达式之上的语法糖 - 如所解释的递归循环 - 你必须通过列表('消耗所有输入')才能到达终点。