序言; if和(停止)递归

时间:2011-03-07 09:40:09

标签: list recursion prolog construction if-statement

为了更好地理解prolog,列表和递归作为一个整体,我正在通过我分配给自己的各种简单任务。 其中包括从列表中删除双重条目。

我已经定义了一条规则:

is_on(Item, [Ah|At]) :- Ah = Item; is_on(Item, At).

检查列表X上是否有“项目”。所以我想我可以扩展它来定义filter_double谓词:

filter_doubles([Ah|At], Result) :-
    (not(is_on(Ah, At)) ->
        Result = [Ah|Result]
    ;
        filter_doubles(At, Result)
    ).

这对我来说非常有意义:如果在列表的其余部分(尾部)中没有出现Ah,则使用列表构造将a添加到结果的前面,否则递归到列表的其余部分。 显然Prolog认为不是这样:

47 ?- filter_doubles([1,2,3,3,4,2,1,1], Z).
Z = [3|**]. 

我觉得这个问题太迫切了吗?

2 个答案:

答案 0 :(得分:3)

在逻辑编程中,谓词中的递归通常用多个规则处理。第一条规则描述了递归基本情况,即其停止条件;其他规则,或者可能只是第二个规则,描述了递归步骤。

因此,您的is_on规则(我已重命名为contains)通常以下列方式编写:

contains(Item, [Item | _]).
contains(Item, [_ | Tail]) :- contains(Item, Tail).

filter_double谓词可能会进行类似的重写。首先,空列表将对应于空结果。

filter_doubles([], []).

然后,如果Item出现在列表的Rest中,则会重复列表的Rest,删除该Item的出现次数。

filter_doubles([Item | Rest], Result) :-
    contains(Item, Rest), !,
    filter_doubles(Rest, Result).

最后,如果列表的Item中没有Rest(因为之前的规则已针对该情况检出过),您可以自由放置Item在使用列表构造的结果的前面,并继续过滤列表的Rest

filter_doubles([Item | Rest], [Item | Tail]) :- filter_doubles(Rest, Tail).

请注意,当您尝试使用Result = [Ah|Result]等表达式执行累积时,Prolog会创建无限递归的数据结构:Result与以Ah为首的列表统一Result为尾部,与以Ah为首,Result为尾的列表统一,与Ah为首和Result的列表统一如尾巴,依此类推,等等。

答案 1 :(得分:2)

你需要在两个分支中递归,你需要一个基本情况:

filter_doubles([], []).
filter_doubles([X|L], Result) :-
    (memberchk(X,L) ->
        filter_doubles(L, Result)
    ;
        filter_doubles(L, Result0),
        Result = [X|Result0]
    ).

Result = [Ah|Result]确实似乎是势在必行的思考。 Prolog中的意思是“将Result与一个以Result作为其第二个参数的术语统一起来”,这个术语要么失败(与发生检查统一),要么产生“理性树”(图形结构)有一个循环,在大多数Prologs)。

练习:使我发布的代码尾递归。

请注意,这会删除除最后一个项目之外的所有项目。