第一个元素的索引大于X(Prolog)

时间:2016-07-09 16:02:34

标签: list prolog clpfd

我知道如何在Prolog中找到特定元素的索引,但有没有办法找到大于say X的数字的第一个实例的索引。例如,假设我有一个列表,但是列表中的某个地方有一个大于1的随机数。我怎样才能找到大于1的数字的第一个实例的索引?我对Prolog很陌生,并且对谓词的子目标不太擅长。

4 个答案:

答案 0 :(得分:5)

您希望在列表 索引之间编写关系。我们称之为list_1stindex_gt / 3。有第四个参数来跟踪当前指数是恰当的。但是,不用这个累加器来打扰用户会很好,所以你可以使用辅助谓词和当前索引的附加参数,让它称之为list_1stindex_gt_ / 4。假设您要开始计算1处的索引(否则将第四个参数更改为0),您可以像这样定义list_1stindex_gt / 3:

:-use_module(library(clpfd)).

list_1stindex_gt(L,I,GT) :-
   list_1stindex_gt_(L,I,GT,1).

对于list_1stindex_gt_ / 4,您有两种情况:

  1. 列表的头部大于第三个参数:然后您知道所需的索引。

  2. 列表的头部小于或等于第三个参数:然后您将累加器增加1并继续搜索列表的尾部。

  3. 你可以像这样在Prolog中写一下:

    list_1stindex_gt_([X|Xs],I,GT,I) :-       % case 1
       X #> GT.
    list_1stindex_gt_([X|Xs],I,GT,Acc0) :-    % case 2
       X #=< GT,
       Acc1 #= Acc0+1,
       list_1stindex_gt_(Xs,I,GT,Acc1).
    

    示例查询:哪个索引是给定列表中第一个大于1的元素?

       ?- list_1stindex_gt([1,1,1,1,5,1,1,2],I,1).
    I = 5 ? ;
    no
    

    第一个元素大于1的索引在三个变量的列表中?

       ?- list_1stindex_gt([A,B,C],I,1).
    I = 1,
    A in 2..sup ? ;
    I = 2,
    A in inf..1,
    B in 2..sup ? ;
    I = 3,
    A in inf..1,
    B in inf..1,
    C in 2..sup ? ;
    no
    

    第一个元素大于变量X的哪个索引可以在三个变量的列表中?

       ?- list_1stindex_gt([A,B,C],I,X).
    I = 1,
    X#=<A+ -1 ? ;
    I = 2,
    X#>=A,
    X#=<B+ -1 ? ;
    I = 3,
    X#>=A,
    X#=<C+ -1,
    X#>=B ? ;
    no
    

    此外,您可以考虑@ mat的建议从this answer到之前的问题的改进:遵循(#<)/3背后的想法,您可以定义(#&gt;)/ 3然后使用if_/3定义list_1stindex_gt_ / 4,如下所示:

    :-use_module(library(clpfd)).
    
    #>(X, Y, T) :-
            zcompare(C, X, Y),
            greater_true(C, T).
    
    greater_true(<, false).
    greater_true(>, true).
    greater_true(=, false).
    
    list_1stindex_gt(L,I,GT) :-
       list_1stindex_gt_(L,I,GT,1).
    
    list_1stindex_gt_([X|Xs],I,GT,Acc0) :-
       if_(X #> GT,
           (I #= Acc0),
           (Acc1 #= Acc0+1, list_1stindex_gt_(Xs,I,GT,Acc1))).
    

    这样第一个查询就会成功,而不会打开不必要的选择点:

    ?- list_1stindex_gt([1,1,1,1,5,1,1,2],I,1).
    I = 5.
    

答案 1 :(得分:4)

这是一个略有不同的看法:

:- use_module(library(clpfd)).
:- use_module(library(lists)).

:- asserta(clpfd:full_answer).

zs_first_greater(Zs, Index, Pivot) :-
   append(Prefix, [E|_], Zs),
   maplist(#>=(Pivot), Prefix),
   E #> Pivot,
   length([_|Prefix], Index).           % 1-based index

使用SICStus Prolog 4.3.3的示例查询:

| ?- zs_first_greater([1,1,1,2,1,1], I, 1).
I = 4 ? ;
no

| ?- zs_first_greater([1,1,1,2,1,1], I, 3).
no

| ?- zs_first_greater([], I, 3).
no

| ?- zs_first_greater([1,1,1,1,5,1,1,2], I, 1).
I = 5 ? ;
no

感谢,我们也可以提出非常一般的问题:

| ?- zs_first_greater([A,B,C,D], I, X).
I = 1,
A#>=X+1,
A in inf..sup,
X in inf..sup ? ;
I = 2,
A#=<X,
B#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup ? ;
I = 3,
A#=<X,
B#=<X,
C#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup,
C in inf..sup ? ;
I = 4,
A#=<X,
B#=<X,
C#=<X,
D#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup,
C in inf..sup,
D in inf..sup ? ;
no

答案 2 :(得分:1)

要获得L中的任何索引,保持元素V大于N,您可以写:

?- L=[1,2,3,1,2,3],N=2, nth1(I,L,V),V>N.

并限制为第一个实例:

?- L=[1,2,3,1,2,3],N=2, once((nth1(I,L,V),V>N)).

如果您有库(clpfd)可用,并且您的列表的域限制为整数,则元素/ 3可以扮演与nth1 / 3相同的角色,从而提供更多的通用性

答案 3 :(得分:0)

这是一个解决方案,正如其他人指出的那样,它不是通用的,只有当整数列表和阈值是基础术语时它才会起作用。

与大多数列表处理谓词一样,我们需要递归地思考它:

  1. 检查列表的标题(第一个元素)。如果它大于提供的阈值,那么我们就完成了。
  2. 否则将步骤1应用于列表的尾部(删除标题后剩余的列表)。
  3. 如果你想要元素的索引(而不是它的实际值),我们还需要跟踪索引并在步骤2中增加它。为此,我们需要一个辅助谓词。

    %
    % Predicate called by the user:
    %
    % The element of List at Index is the first one greater than Threshold.
    %
    idx_first_greater(List, Threshold, Index) :-
       % here we use our helper predicate, initializing the index at 1.
       idx_first_greater_rec(List, Threshold, 1, Index).
    
    %
    % Helper predicate:
    %
    % idx_first_greater_rec(List, Threshold, CurIdx, FoundIdx) :
    %    The element of List at FoundIndex is the first one greater
    %    than Threshold. FoundIdx is relative to CurIdx.
    %
    
    % Base case. If the header is greater than the Threshold then we are done.
    % FoundIdx will be unified with CurIdx and returned back to the recursion stack.
    idx_first_greater_rec([H|_], Threshold, Index, Index) :- H > Threshold, !.
    
    % Recursion. Otherwise increment CurIdx and search in the tail of the list
    idx_first_greater_rec([_|T], Threshold, CurIdx, FoundIdx) :-
       NewIdx is CurIdx+1,
       idx_first_greater_rec(T, Threshold, NewIdx, FoundIdx).
    

    注意:

    1. 如果传递空列表或者未找到大于阈值的元素,则谓词将失败。这对我来说就像一个好的行为。
    2. 此解决方案是尾递归的,因此可以由Prolog自动优化。
    3. 示例输出:

      ?- idx_first_greater([1,1,1,2,1,1], 1, Idx).
      Idx = 4 ;
      false.
      
      ?- idx_first_greater([1,1,1,2,1,1], 3, Idx).
      false.
      
      ?- idx_first_greater([], 3, Idx).
      false.