How to build a list of positions for a specific item in the main list?
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 ...
source to share
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
bothY
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 elementsE
?
?- 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.
source to share
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].
source to share