为了更好地理解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|**].
我觉得这个问题太迫切了吗?
答案 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)。
练习:使我发布的代码尾递归。
请注意,这会删除除最后一个项目之外的所有项目。