SWI-Prolog排序谓词不起作用

时间:2014-05-03 00:07:18

标签: prolog

我刚刚编写了一个程序,执行以下任务:"获取元素,其值等于其索引"。

以下是代码:

% get element's index
get_index([Element|_], Element, 0).
get_index([_|T], Element, Index) :-
    get_index(T, Element, Index1),
    Index is Index1+1.

index_equals_to_element(List, List2) :-
    member(X, List),
    get_index(List, X, Index),
    Index =:= X,
    append([], [X], List2).

效果很好。但是有一个问题。对于列表 [0,3,2,4,0] ,我的谓词index_equals_to_element会返回 [0,2,0]

好的,让它发生。但是,当我尝试仅输出唯一元素时,我会在没有任何更改的情况下获得相同的列表。例如:

?- index_equals_to_element([0, 3, 2, 4, 0], List).
% Outputs [0, 2, 0]

?- sort(List, List2).
% Outputs [0, 2, 0] either, when expected [0, 2]

对我来说这很奇怪,因为这很好用:

?- sort([0, 2, 1, 0], List).
% Outputs [0, 1, 2].

为什么sort不能仅使用我的谓词生成的列表?

3 个答案:

答案 0 :(得分:4)

一个简单的解决方案是:

index_equals_to_element(List1, List2) :-
    % assume that list position index starts at 0
    index_equals_to_element(List1, 0, List2).

index_equals_to_element([], _, []).
index_equals_to_element([X| Xs], Index, List2) :-
    NextIndex is Index + 1,
    (   X == Index ->
        List2 = [X| Tail],
        index_equals_to_element(Xs, NextIndex, Tail)
    ;   index_equals_to_element(Xs, NextIndex, List2)
    ).

示例电话:

?-  index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0, 2].

我建议您通过输入查询来使用Prolog系统的跟踪功能来研究它:

?-  trace, index_equals_to_element([0, 3, 2, 4, 0], List).

执行步骤,直到您明确谓词定义。

答案 1 :(得分:3)

您的index_equals_to_element([0, 3, 2, 4, 0], List).未按照您的要求输出[0, 2, 0],但会给出三个答案[0][2][0]

?- index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0] ;
List = [2] ;
List = [0] ;
false.

您可以使用findall来获得所需内容:

?- findall(X, index_equals_to_element([0, 3, 2, 4, 0], [X]), List).
List = [0, 2, 0].

更新。以下是我认为index_equals_to_element/2更好的实施方式:

index_equals_to_element(List, List2) :-
    index_equals_to_element(List, 0, List2).

index_equals_to_element([], _, []).
index_equals_to_element([X | Rest], I, Rest2) :-
    Inext is I + 1,
    index_equals_to_element(Rest, Inext, NewRest),
    ( X =:= I ->
        Rest2 = [X | NewRest]
    ; 
        Rest2 = NewRest
    ).

试运行:

?- index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0, 2].

?- index_equals_to_element([0, 1, 2, 2, 4, 5], List).
List = [0, 1, 2, 4, 5].

答案 2 :(得分:2)

其他答案最适合学习Prolog的基本要点。但是,使用SWI-Prolog findall/3中的高阶谓词nth0/3library(lists),这是一个更简洁(但也更容易理解)的解决方案:

elements_equal_to_index(List, Elements) :-
    findall(Index, nth0(Index, List, Index), Elements).

修改

正如@Paulo Moura在评论中所指出的,如果所有参数都被实例化,上述答案仅相当于此处提供的其他答案。即,如果上面遇到列表中的自由变量,我会将该变量绑定到列表中的索引,而不是将其拒绝为不满意的元素。为索引和列表元素之间的强相等性添加测试应该使答案符合:

elements_equal_to_index(List, Elements) :-
    findall( Index,
             ( nth0(Index, List, Elem),
               Elem == Index ),
             Elements
           ).