【问题标题】:Why does Prolog repeat the last value of my list after semicolon?为什么 Prolog 在分号后重复我列表的最后一个值?
【发布时间】:2023-04-08 02:31:01
【问题描述】:

我正在尝试在 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?

【问题讨论】:

    标签: list recursion prolog


    【解决方案1】:

    你的 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].
    

    确切地说,即使要删除的元素是其成员,您的 remove/3 的第一个子句也会通过保持 list 完整而产生不正确的解决方案:

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

    另外,请记住,许多 Prolog 实现将捆绑 remove/3 和 min/2 实现(当然还有 sort)。例如,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 的调用,以找出问题所在。

    【讨论】:

    • remove/3的第一个子句,你指的是remove(_,[],[])吗?我认为如果试图从空列表中删除任何内容,这只会返回一个空列表。如果元素不属于,我认为这将是一个很好的最后递归步骤。
    • 另外,在您编写的删除规则中,remove(1,[2],List) 返回 List = []。我不希望单个元素列表在没有正确调用的情况下丢失其项目。
    • 看来你是对的,我最初的 remove/3 实现被破坏了,抱歉。现在已修复(希望如此),我已经更新了答案。另外,我提到的 delete/3 也与我们的 remove/3 不同,它会“立即”删除所有出现的元素,而 remove/3 在回溯过程中将它们一一删除。
    • 根据您的问题 - 是的,我指的是 remove(_,[],[])。它确实是必需的,但如果根据您的实现使用它也会产生不正确的解决方案。新的实现(见更新的答案)仅在我们到目前为止还没有发现任何要删除的元素时才允许使用它。换句话说,如果列表中没有我们要删除的元素 x,我们允许使用 remove(_,[],[])。否则(我们已经找到 x,但只是想继续回溯,看看是否能找到更多解决方案),我们不允许使用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2018-12-02
    • 2016-05-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多