为什么Prolog在分号后重复列表的最后一个值?

时间:2017-10-19 03:58:17

标签: list recursion prolog

我正在尝试在Prolog中创建自己的排序规则,经过大量的反复试验后,我能够让它工作,除非我按下;在swipl中,它会将列表的最后一个值添加到列表中。

使用的代码如下:

min在列表中找到最小值并将其返回

min([H|[]],H).
min([H|T],Min) :- 
    min(T,CurrentMin),
    H < CurrentMin,
    Min = H.
min([H|T],Min) :- 
    min(T,CurrentMin),
    CurrentMin =< H,
    Min = CurrentMin. 

remove在列表中查找要删除的元素,并返回一个删除了元素的列表

remove(_,[],[]).
remove(First,[First|Rest],Rest).
remove(Element,[First|Rest],[First|RestLessElement]) :-
    remove(Element,Rest,RestLessElement).

最后,sort_inc尝试使用上述规则按递增顺序创建排序列表。

sort_inc([H|[]],[H]).
sort_inc(UnOrderedList,[H|OrderedTail]) :-
    min(UnOrderedList,H),
    remove(H,UnOrderedList,T),
    sort_inc(T,OrderedTail).

排序效果很好,但是当我在列表上运行规则后按swipl中的分号时,它会重复列表中的最后一个值:

sort_inc([3,6,8,4],List).
List = [3, 4, 6, 8] ;
List = [3, 4, 6, 8, 8] ;
List = [3, 4, 6, 8, 8, 8] ;
List = [3, 4, 6, 8, 8, 8, 8] ;
List = [3, 4, 6, 8, 8, 8, 8, 8] ;
List = [3, 4, 6, 8, 8, 8, 8, 8, 8] ;
List = [3, 4, 6, 8, 8, 8, 8, 8, 8|...] .

为什么重复这最后一个值而不是在输入分号时返回false?

1 个答案:

答案 0 :(得分:4)

你的remove / 3实现中有问题,请尝试这样做:

remove(X, [X|Rest], Result) :-
    !, remove_sub(X, [X|Rest], Result).
remove(X, [Y|Rest], Z) :-
    X \= Y,
    remove(X, Rest, RestRemoved),
    Z = [Y|RestRemoved].
remove(_, [], []).

remove_sub(X, [X|Rest], Rest).
remove_sub(X, [Y|Rest], Z) :-
    remove_sub(X, Rest, RestRemoved),
    Z = [Y|RestRemoved].

确切地说,删除/ 3的第一个子句会使列表保持不变,即使要删除的元素是其成员,也会生成不正确的解决方案:

?- remove(1, [1, 2, 3], L).
L = [2, 3] ;
L = [1, 2, 3].

另外,请记住,许多Prolog实现将捆绑删除/ 3和min / 2实现(当然还有排序)。例如,Swi-Prolog包括:

  • delete / 3,它几乎与你的remove / 3应该做的事情有关,除了它一次删除所有元素实例
  • select / 3,它几乎完成了你的remove / 3应该做的事情,除了它不允许删除列表中不存在的元素
  • min_list / 2和min_member / 2,查找列表的最小元素
  • sort / 2,msort / 2,predsort / 3,进行实际排序

即使您的目标是创建自定义实现,仍然值得使用它们来测试您自己的实现,例如您可以使用delete / 3快速替换对remove / 3的调用,以找出问题所在。