Prolog every ith element in sublist - prolog

Is it possible to copy every ith element from a list to a sublist with just one ternary predicate sublist(Element, List1, List2) and built-in length and append?
I know, with 4-element auxiliary predicates, it becomes rather trivial, but it isn't what I need.
Can anybody suggest a strategy?

with some builtins a declarative solution is really easy:
sublist(Element, List1, List2) :-
findall(E, (nth1(I, List1, E), 0 is I mod Element), List2).
while explicitly iterating:
sublist(Element, List1, List2) :-
( N is Element-1,
length(T, N),
append(T, [E|R], List1)
-> sublist(Element, R, ListR),
List2 = [E|ListR]
; List2 = []
).
where you can see the how to use Boris' suggestion

You can use lentgth/2 with the first argument a variable and the second an integer, to create a list of not instantiated variables, like this:
?- length(L, 4).
L = [_G936, _G939, _G942, _G945].
If you now use append/3 with this list as the first argument, and a variable as the second, it will split the list in your third argument:
?- length(A, 4), append(A, B, [a,b,c,d,e,f,g,h]).
A = [a, b, c, d],
B = [e, f, g, h].
So if you want say the 5th element of a list, you could take the head of the second argument:
?- length(A, 4), append(A, [Fifth|Rest], [a,b,c,d,e,f,g,h]).
A = [a, b, c, d],
Fifth = e,
Rest = [f, g, h].
This is not a solution but a valid strategy:
every_ith(I, In, [X|Ys]) :-
N is I - 1,
length(Prefix, N),
append(Prefix, [X|Xs], In),
every_ith(I, Xs, Ys).

Related

Swap function in prolog, infinite loop

I am trying to create a swap function in prolog but I ended up with an infinite loop, I tried to debug it using trace()
An example of this function is swap(4, 3, ["You", "Are", "Awesome", "thank", "You"], SwappedList)
With the output being
["You", "Are", "thank", "Awesome", "You"]
In the trace output, it is showing that the problem is in the delete as it is failing and redoes the split
/* Getting the nth element of the list*/
n_thelement(1, [Head|_], Head).
n_thelement(N, [_|Tail], Item):-
NewN is N-1,
n_thelement(NewN, Tail, Item).
/* Deleting the element of the desired Nth element*/
delete(X, [X|Tail], Tail).
delete(X, [Head|Tail], [Head|Item]):-
delete(X, Tail, Item).
/* Adding the deleted element to the beginning of the list*/
append([], Element, Element).
append([Head], Element, [Head|Element]).
swap(X, X, List, List).
swap(X, Y, List, NList):-
n_thelement(X, List, Num1),
n_thelement(Y, List, Num2),
split(X, List, B1, A1),
delete(Num1, A1, L1),
append([Num2], L1, NList1),
append(B1, NList1, NList2),
split(Y, NList2, B2, A2),
delete(Num2, A2, L2),
append([Num1], L2, NList3),
append(B2, NList3, NList).
split(1, [Head|Tail], Head, Tail).
split(N, [Old_List|New_List], Old_List, New_List):-
NewN is N -1,
split(NewN, _, Old_List, New_List).
If I understand your problem statement correctly, given to indices into a list, M and N such that M < N and M and N are both valid indices into the list, you want to swap the elements at those indices.
I would first make the indices zero-relative instead of 1-relative as that makes the math a little easier.
So, you want to break up the list into 5 pieces, 3 of which are themselves lists of any length and two of which are the list entries to be swapped:
As: The lead-in prefix of the list. It is of length M.
B: The 1st item to be swapped.
Cs: The middle segment of the list. It is of length N - (M+1).
D: The 2nd item to be swapped.
Es: The suffix/remainder of the list. It is of any length.
append/3 is useful for deconstruction and reconstruction of lists, making the actual swap easy. You have 3 cases.
First, the special case of both indices being the same, in which case, there is no work to do:
swap( M, M, Ls, Ls ).
Second, the case of the indices being out of order, in which case we just recursively swap them to put them in order:
swap( M, N, Ls, Rs ) :- M > N, swap(N,M,Ls,Rs).
Third, the general case:
swap( M, N, Ls, Rs ) :- % if the 2 indices differ
M < N, % - and are in order
M >= 0, % - and M is greater than or equal to zero
N >= 0, % - and N is greater than or equal to zero
X is N - (M+1), % - compute the length of the middle segment
length( As, M ), % - construct an empty, unbound list of length M, the length of the prefix
length( Cs, X ), % - and construct an empty, unbound list of that length
append( As, [B|T1], Ls), % - get the prefix (As) and the first item (B) to be swapped
append( Cs, [D|Es], T1), % - get the middle segment (Cs), the second item (D) to be swapped, and the suffix (Es)
append( As, [D|Cs], T2), % - concatenate As, D, and Cs, then...
append( T2, [B|Es], Rs ) % - concatenate that with B and the suffix
. % Easy!
You can define a predicate to replace the i-th item of the list for another:
replace(Index, [Old|Rest], [New|Rest], Old, New) :- Index == 0, !.
replace(Index, [First|Rest], [First|NewRest], Old, New) :-
Index > 0,
Previous is Index - 1,
replace(Previous, Rest, NewRest, Old, New).
Examples:
?- replace(1, [a,b,c,d,e], List1, Old1, x).
List1 = [a, x, c, d, e],
Old1 = b.
?- replace(1, [a,b,c,d,e], List1, Old1, New1).
List1 = [a, New1, c, d, e],
Old1 = b.
?- replace(4, [a,b,c,d,e], List2, Old2, New2).
List2 = [a, b, c, d, New2],
Old2 = e.
Then, using this predicate, you can define:
swap(I, J, OldList, NewList) :-
replace(I, OldList, List, X, Y),
replace(J, List, NewList, Y, X).
Examples:
?- swap(3, 2, ["You", "Are", "Awesome", "thank", "You"], L).
L = ["You", "Are", "thank", "Awesome", "You"].
?- swap(1, 4, [a,b,c,d,e], L).
L = [a, e, c, d, b].
?- swap(0, 3, [a,b,c,d,e], L).
L = [d, b, c, a, e].
?- swap(1, 0, [a,b,c,d,e], L).
L = [b, a, c, d, e].
?- swap(2, 2, [a,b,c,d,e], L).
L = [a, b, c, d, e].
?- swap(3, 9, [a,b,c,d,e], L).
false.

Get set of elements from list (Prolog)

I am trying to get a set of elements from a list in prolog, such that a query:
get_elems([1, 2, 4, 10], [a, b, c, d, e], X).
yields:
X = [a, b, d]
I would like to implement it without using the built in predicate nth.
I have tried using the following, but it does not work:
minus_one([], []).
minus_one([X|Xs], [Y|Ys]) :- minus_one(Xs, Ys), Y is X-1.
get_elems([], _, []).
get_elems(_, [], []).
get_elems([1|Ns], [A|As], Z) :- get_elems(Ns, As, B), [A|B] = Z.
get_elems(Ns, [_|As], Z) :- minus_one(Ns, Bs), get_elems(Bs, As, Z).
Edit: The list of indices is guaranteed to be ascending, also I want to avoid implementing my own version of nth.
Give this a go:
get_elems(Xs,Ys,Zs) :- get_elems(Xs,1,Ys,Zs).
get_elems(Xs,_,Ys,[]) :- Xs = []; Ys = [].
get_elems([N|Xs],N,[H|Ys],[H|Zs]) :- !, N1 is N + 1, get_elems(Xs,N1,Ys,Zs).
get_elems(Xs,N,[_|Ys],Zs) :- N1 is N + 1, get_elems(Xs,N1,Ys,Zs).
This just keeps counting up and when the head of the second term is equal to the current index it peels off the head and makes it the head of the current output term. If it doesn't match it just discards the head and keeps going.

Why the end of result list is not []?

I tried to output a list only contain the unique element. But the result is not perfect.
list_concatenation([],L,L).
list_concatenation([X1|L1],L2,[X1|L3]) :-
list_concatenation(L1,L2,L3).
list_member(X, [X|_]).
list_member(X, [_|TAIL]) :- list_member(X,TAIL),!.
toset([],_).
toset([X|TAIL],SET):-
list_member(X,SET),
toset(TAIL,SET).
toset([X|TAIL],SET):-
\+ list_member(X,SET),
list_concatenation([X],SET,NEWSET),
toset(TAIL,NEWSET).
For example:
?- toset([1,1,2,3],X).
the result should be 'X = [1, 2, 3]' but now, it is 'X = [1, 2, 3|_16998]'
You actually implement the toset/2 the wrong way. You actually use list_member/2 here to add an element to the list. Indeed, if you use a free variable, you get:
?- list_member(2, L).
L = [2|_3616] ;
L = [_3614, 2|_3622].
The list_member/2 itself, is not correctly implemented as well. The cut (!) here prevents to keep yielding values. You thus should remove the cut:
list_member(X, [X|_]).
list_member(X, [_|Tail]) :-
list_member(X,Tail).
This thus can yield all possible lists where 2 is a member:
?- list_member(2, L).
L = [2|_3412] ;
L = [_3410, 2|_3418] ;
L = [_3410, _3416, 2|_3424] ;
L = [_3410, _3416, _3422, 2|_3430] ;
...
But now the problem still remains: you should not use list_member/2 here to add elements to the list, you can use an accumulator here that keeps track of the elements already yielded, and then eventually return that accumulator, like:
toset(L, S) :-
toset(L, [], S).
toset([], A, S):-
reverse(A, S).
toset([H|T], A, L) :-
( member_list(H, A)
-> toset(T, [H|A], L)
; toset(T, A, L)
).
We thus keep prepending the values to the accumulator, and in the end, we reverse/2 [swi-doc] the accumulator, and return that as a result.

Prolog: how can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ;?

Prolog: How can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ; ? The program works well as long as I press ; in the command line, but I want to return directly a list of pairs.
comb(1, [H|_], [H]).
comb(N, [H|T], [H|C]) :- N1 is N - 1, N1 > 0, comb(N1, T, C).
comb(N, [_|T], C):- comb(N, T, C).
This is my program. Thank you very much!
You are looking for findall/3.
findall(+Template, :Goal, -Bag)
Create a list of the instantiations Template gets successively on backtracking over Goal and unify the result with Bag. Succeeds with an empty list if Goal has no solutions. findall/3 is equivalent to bagof/3 with all free variables bound with the existential operator (^), except that bagof/3 fails when Goal has no solutions.
Example:
?- findall(X, comb(2, [a,b,c,d], X), Xs).
Xs = [[a, b], [a, c], [a, d], [b, c], [b, d], [c, d]].

Select N elements from a List in Prolog

I'm trying to write a Prolog predicate (SWI) that would select N elements from a List, like this:
selectn(+N, ?Elems, ?List1, ?List2) is true when List1, with all Elems removed, results in List2.
selectn(N,Lps,L1s,[]) :- length(L1s,L), N >= L, permutation(L1s,Lps).
selectn(0,[],L1s,Lps) :- permutation(L1s,Lps).
selectn(N,[E|Es],L1s,L2s) :-
select(E,L1s,L0s),
N0 is N-1,
selectn(N0,Es,L0s,L2s).
My problem is that in some cases, I get duplicated results and I don't know how to avoid them:
?- findall(L,selectn(2,Es,[a,b,c],L),Ls),length(Ls,Solutions).
Ls = [[c], [b], [c], [a], [b], [a]],
Solutions = 6.
This is no homework, but if you want to help me as if it was, I'll be pleased as well.
this could answer your question (albeit I don't understand your first clause selectn/4, permutation is already done by 'nested' select/3)
selectn(0, [], Rest, Rest).
selectn(N, [A|B], C, Rest) :-
append(H, [A|T], C),
M is N-1,
selectn(M, B, T, S),
append(H, S, Rest).
yields
?- findall(L,selectn(2,Es,[a,b,c],L),Ls),length(Ls,Solutions).
Ls = [[c], [b], [a]],
Solutions = 3.

Resources