Swap function in prolog, infinite loop - prolog

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.

Related

Prolog How to write a predicate sum 3 max values in list?

How to write a predicate sum 3 max values in list?
max3(L,X)
Example:
max3([1,7,9,3,5],X).
X = 21.
As a starting point:
% Can potentially change order of the list in Rest
list_max_rest([H|T], Max, Rest) :-
list_max_rest_(T, H, Max, Rest).
list_max_rest_([], Max, Max, []).
list_max_rest_([H|T], P, Max, [P|Rest]) :-
H #> P,
!,
list_max_rest_(T, H, Max, Rest).
list_max_rest_([H|T], P, Max, [H|Rest]) :-
list_max_rest_(T, P, Max, Rest).
Usage:
?- list_max_rest([2,1,200,9], Max, Res).
Max = 200,
Res = [1, 2, 9].
Use that 3 times...
max3(Ls, X) :-
select(A, Ls, Ls2),
select(B, Ls2, Ls3),
select(C, Ls3, Ls4),
A >= B,
B >= C,
\+ (member(Q, Ls4), Q > C),
X is A+B+C.
Take A from the list, B from the remainder, C from that remainder, they must be A>=B>=C, and there must not be a member Q left in the remainder which is bigger than C. Add those up.
This is not efficient; brebs' suggestion of:
max3(Ls, X) :-
sort(0, #>=, Ls, [A,B,C|_]),
X is A+B+C.
is neater

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.

Prolog every ith element in sublist

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

How to take the head of one list in a list of lists and put it in another list?

I have a list of lists, which looks something like this:
[[b,c],[],[a]]
I want to write a predicate that will take a specific letter from the top of one of the lists, and put it in another list. The letter to be moved is specified beforehand. It can be placed on top of a list which is either empty, or contains a letter that is larger (b can be placed on c, but not otherwise). The letter should be removed from the original list after it has been moved.
I am having trouble telling Prolog to look for a list which starts with the specified letter, and also how to tell Prolog to put this in another list.
here is my solution, based no [nth1][1]/4 (well, you should read documentation for nth0/4, really)
/* takes a specific letter from the top of one of the lists, and puts it in another list.
The letter to be moved is specified beforehand.
It can be placed on top of a list which is either empty, or contains a letter that is larger (b can be placed on c, but not otherwise).
The letter should be removed from the original list after it has been moved.
*/
move_letter(Letter, Lists, Result) :-
% search Letter, Temp0 miss amended list [Letter|Rest]
nth1(I, Lists, [Letter|Rest], Temp0),
% reinsert Rest, Temp1 just miss Letter
nth1(I, Temp1, Rest, Temp0),
% search an appropriate place to insert Letter
nth1(J, Temp1, Candidate, Temp2),
% insertion constraints
J \= I, (Candidate = [] ; Candidate = [C|_], C #> Letter),
% update Result
nth1(J, Result, [Letter|Candidate], Temp2).
Usage examples:
?- move_letter(a,[[b,c],[],[a]],R).
R = [[a, b, c], [], []] ;
R = [[b, c], [a], []] ;
false.
?- move_letter(b,[[b,c],[],[a]],R).
R = [[c], [b], [a]] ;
false.
I followed this 'not idiomatic' route to ease the check that the insertion occurs at different place than deletion.
Below are some rules to find lists that starts with some element.
starts_with([H|T], H).
find_starts_with([],C,[]).
find_starts_with([H|T],C,[H|Y]) :- starts_with(H,C),find_starts_with(T,C,Y).
find_starts_with([H|T],C,L) :- \+ starts_with(H,C), find_starts_with(T,C,L).
Example:
| ?- find_starts_with([[1,2],[3,4],[1,5]],1,X).
X = [[1,2],[1,5]] ? ;
I like #CapelliC's concise solution. Here's an alternative solution that doesn't use the nth1 built-in. Apologies for the sucky variable names.
% move_letter : Result is L with the letter C removed from the beginning
% of one sublist and re-inserted at the beginning of another sublist
% such that the new letter is less than the original beginning letter
% of that sublist
%
move_letter(C, L, Result) :-
removed_letter(C, L, R, N), % Find & remove letter from a sublist
insert_letter(C, R, 0, N, Result). % Result is R with the letter inserted
% removed_letter : R is L with the letter C removed from the beginning of a
% sublist. The value N is the position within L that the sublist occurs
%
removed_letter(C, L, R, N) :-
removed_letter(C, L, R, 0, N).
removed_letter(C, [[C|T]|TT], [T|TT], A, A).
removed_letter(C, [L|TT], [L|TTR], A, N) :-
A1 is A + 1,
removed_letter(C, TT, TTR, A1, N).
% Insert letter in empty sublist if it's not where the letter came from;
% Insert letter at front of a sublist if it's not where the letter came from
% and the new letter is less than the current head letter;
% Or insert letter someplace later in the list of sublists
%
insert_letter(C, [[]|TT], A, N, [[C]|TT]) :-
A \== N.
insert_letter(C, [[C1|T]|TT], A, N, [[C,C1|T]|TT]) :-
A \== N,
C #< C1.
insert_letter(C, [L|TT], A, N, [L|TTR]) :-
A1 is A + 1,
insert_letter(C, TT, A1, N, TTR).
Results in:
| ?- move_letter(a, [[b,c],[],[a]], R).
R = [[a,b,c],[],[]] ? a
R = [[b,c],[a],[]]
no
| ?- move_letter(b, [[b,c],[],[a]], R).
R = [[c],[b],[a]] ? a
no
| ?- move_letter(b, [[b,c], [], [a], [b,d]], R).
R = [[c],[b],[a],[b,d]] ? a
R = [[b,c],[b],[a],[d]]
no

Arrangements of elements of a list in prolog

domains
el=integer
list = el*
lista = list*
predicates
aux(list,integer,list)
arrangements(list,integer,lista)
clauses
aux([H|_],1,[H]).
aux([_|L],N,L1):-
aux(L,N,L1).
aux([H|L],N,[H|L1]):-
N<>1,
N1=N-1,
aux(L,N1,L1).
arrangements(L,N,R):-
findall(X,aux(L,N,X),R).
This code shows all the combinations of elements of a list. How should I modify it to show the arrangements. I have no ideas
arrangements
[2,3,4] K=2 => [[2,3], [3,2], [2,4], [4,2], [3,4], [4,3]]
combinations
[2,3,4] K=2 => [[3,4], [2,3], [2,4]]
Use select in aux/3 to get any of the permutations from the list:
aux(L, N, [H|T]) :-
N > 1,
select(H, L, M), % Select an element "H" from "L", leaving "M"
N1 is N-1, % NOTE the "is" here, not "=" !!
aux(M, N1, T). % Recurse with remaining elements "M" and count-1
aux(L, 1, [X]) :- member(X, L).
arrangements(L, N, R):-
findall(X, aux(L, N, X), R).
Resulting in:
| ?- arrangements([2,3,4], 2, R).
R = [[2,3],[2,4],[3,2],[3,4],[4,2],[4,3]]
yes

Resources