Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Index of first element greater than X (Prolog)

Tags:

list

prolog

clpfd

I am aware on how to find the index of a specific element in Prolog but is there a way to find the index of the first instance of a number greater than say X. For instance, say I have a list of all ones but there is a random number greater than one somewhere in the list. How could I go about finding the index of the first instance of a number greater than 1? I am really new to Prolog and am not too good at subgoals of predicates.

like image 493
bgibers Avatar asked Dec 24 '22 03:12

bgibers


1 Answers

You want to write a relation between a list an index and a value. Let's call it list_1stindex_gt/3. It is opportune to have a fourth argument to keep track of the current index. However, it would be nice to not bother the user with this accumlator, so you could use and auxiliary predicate with an additional argument for the current index, let's call it list_1stindex_gt_/4. Assuming you want to start counting the indices at 1 (otherwise change the fourth argument to 0) you can define list_1stindex_gt/3 like so:

:-use_module(library(clpfd)).

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

For list_1stindex_gt_/4 you have 2 cases:

  1. The head of the list is greater than the third argument: Then you know the desired index.

  2. The head of the list is smaller or equal to the third argument: Then you increment the accumlator by 1 and continue the search in the tail of the list.

You can write that in Prolog like so:

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).

Example queries: At which index is the first element greater than 1 in the given list?

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

At which index can the first element greater than 1 be in a list of three variables?

   ?- 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

At which index can the first element greater than the variable X be in a list of three variables?

   ?- 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

Furthermore, you could consider @mat's suggested improvement from this answer to a previous question by you: Following the idea behind (#<)/3 you can define (#>)/3 and then define list_1stindex_gt_/4 using if_/3 like so:

:-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))).

This way the first query succeeds without leaving unnecessary choice points open:

?- list_1stindex_gt([1,1,1,1,5,1,1,2],I,1).
I = 5.
like image 50
tas Avatar answered Jan 19 '23 07:01

tas