How to build a list of positions for a specific item in the main list?

How to write a predicate list_pos(B,E,L)

that returns positions E

in B

, in a named list L

(given that the first element B

has position = 0), I tried to write a program like this, but it did not work successfully. thank

+3


source to share


5 answers


Here's a solution that's a little more general than @ Jerome's:

list_pos(Xs, E, Ps) :-
   ( ground(Xs) -> true ; throw(error(instantiation_error,_)) ),
   ( length(Xs,_) -> true ; throw(error(type_error(list,Xs),_)) ),
   bagof(P, nth0(P, Xs, E), Ps).

?- list_pos([a,b,c,c,a,a], E, Rs).
   E = a, Rs = [0, 4, 5]
;  E = b, Rs = [1]
;  E = c, Rs = [2, 3].

      

So you don't even need to specify the exact element you want. Instead, you ask:



What are the occurrences of the various elements?

This can be generalized one step further ...

+2


source


Here's a generic and clean solution - I'd put a bounty here to put this in more comfortable abstractions.

list_pos(Xs, E, Ps) :-
   list_pos(Xs, E, Ps, 0).

list_pos([], _E, [], _P).
list_pos([X|Xs], X, [P0|Ps], P0) :-
   P1 is P0 + 1,
   list_pos(Xs, X, Ps, P1).
list_pos([X|Xs], E, Ps, P0) :-
   dif(X, E),        % element must be different
   P1 is P0 + 1,
   list_pos(Xs, X, Ps, P1).

      

And here's a more compact and efficient way to list_pos/4

use if_/3

and re-equality (=)/3

. Prolog is a if_/3

little different from traditional if-then-else in that it can choose both Then and Else. Maybe just try if_/3

:

?- if_( X=Y, Diagnosis=equal, Diagnosis=inequal).
X = Y,
Diagnosis = equal ;
Diagnosis = inequal,
dif(X, Y).

      

Informally, we ask here:

Are X

both Y

equal or not?

And Prolog's answer is not just yes or no, but:

Yes they are equal if they are equal



AND

No, they are not equal if they are different

Sounds annoying? Well, if we ask general questions that don't contain enough information, we shouldn't be surprised to get similar general answers!

list_pos([], _E, [], _P).
list_pos([X|Xs], E, Ps0, P0) :-
   P1 is P0+1,
   if_(X = E, Ps0 = [P0|Ps1], Ps0 = Ps1),
   list_pos(Xs, E, Ps1, P1).

      

And now let's try something very general!

What should the list look like [X,Y,Z]

so that it has two elements E

?

?- list_pos([X,Y,Z],E,[A,B]).
   X = Y, Y = E, % ... the first two are the same
   A = 0, B = 1,
   dif(Z, E)     % ... but the last is different
;  X = Z, Z = E, % ... the first and last are the same
   A = 0, B = 2,
   dif(Y, E)     % ... but the middle element is different
;  Y = Z, Z = E, % ... the last two are the same
   A = 1, B = 2,
   dif(X, E)     % ... and the first is different.
;  false.

      

Note that these answers contain complete generality of all three possible lists of elements! They are all included.

+2


source


Thanks to @false and @Jerome, I am learning a lot. However, I'll post my efforts:

    list_pos(List,Elem,Result) :-
            list_pos(List,Elem,[],-1,R),
            reverse(R,Result),
            !.

    list_pos([],_,W,_,W).
    list_pos([X|Xs],X,Zs,P,R) :-
            Pos is P + 1,
            list_pos(Xs,X,[Pos|Zs],Pos,R).
    list_pos([_|Xs],Y,Zs,P,R) :-
            Pos is P + 1,
            list_pos(Xs,Y,Zs,Pos,R).

      

Ref.

    ?- list_pos([],a,R).
    R = [].

    ?- list_pos([a,c,a,d,e,f],a,R).
    R = [0, 2].

    ?- list_pos([a,b,c,d,e,f,g],g,R).
    R = [6].

    ?- list_pos([a,a,a,a,a,a,a,a,a],a,R).
    R = [0, 1, 2, 3, 4, 5, 6, 7, 8]. 

      

+1


source


I tried:

    list_pos(1,Match,[Match|_]).
    list_pos(Pos,Element,[_|Tail]) :-
             list_pos(P,Element,Tail),
             Pos is P + 1.

      

Ref.

    ?- list_pos(B,a,[a]).
    B = 1 .

    ?- list_pos(B,a,[b,a]).
    B = 2 .

    ?- list_pos(B,a,[b,c,d,e,a,f]).
    B = 5 .

      

0


source


To create a list of all results, use findall

:

list_pos(List, Element, Result) :-
  findall(Position, nth0(Position, List, Element), Result).

      

0


source







All Articles