Prolog creating lists - list

I'm trying to write a program in Prolog that will take in three lists (all of which are the same length) and return a list of lists.
The list of lists that I am returning is a triple that contains elements from the three lists that are being passed in. The first element of the triple is from the first list passed in, the second element of the triple is from the second list, and the third element of the triple is from the third list passed in.
What I want to have happen is the list of triples that the function is returning to return every single possible combination that you could make from the three lists being passed in.
As of now I have some code that takes the first elements of the three lists and makes a triple out of them, then takes the second element of all the lists and makes a triple out of them, and so on. Here it is below.
listCombos( [], [], [], []).
listCombos( [A|AREST], [B|BREST], [C|CREST], [[A,B,C]|SOLUTION]) :-
listCombos( AREST, BREST, CREST, SOLUTION).
My strategy for getting every combo is taking the first element of the first list and the first element in the second list and then going through each elements in the third list. Once I have done that I will move on the the first element in the first list and the second element in the second list and match those up with each element in the third list. Then after I have went through the second list move onto the first list. Let me know if more clarification on this is needed.
I'm new to Prolog so I don't understand how to turn what I'm planning to do into code. I've tried a few things but haven't been successful and have gotten some error codes I don't understand so it's hard to tell if I'm going in the right direction (I can post some of my attempts if needed). If anyone has some idea of what direction I should go in or some explanation on what I need to do that would be appreciated.
Thank you very much.

Knowing a little Prolog the most obvious solution is something like this:
listCombos(Xs, Ys, Zs, Result) :-
findall([X,Y,Z],
(member(X, Xs), member(Y, Ys), member(Z, Zs)),
Result).

It's advisable to generalize the construct you're looking for, accepting a list of lists to be combined, following the schema from this answer:
combine(Ls,Rs) :- maplist(member,Rs,Ls).
listCombos(A,B,C, SOLUTION) :- findall(R,combine([A,B,C],R),SOLUTION).

We first can solve a related problem: given a list of "heads" Hs and a list of "tails" Ts, construct all lists for all heads H in Hs, and all tails T in Ts in a list. We can do this with a predicate:
merge_all([], _, []).
merge_all([H|Hs], Ts, All) :-
merge_single(Ts, H, All, D),
merge_all(Hs, Ts, D).
merge_single([], _, D, D).
merge_single([T|Ts], H, [[H|T]|Rest], D) :-
merge_single(Ts, H, Rest, D).
For example:
?- merge_all([a, b], [[1, 4], [2, 5]], R).
R = [[a, 1, 4], [a, 2, 5], [b, 1, 4], [b, 2, 5]].
Now we can use this for example to make all cross products with Cs and the "empty set", for example if Cs = [a, b, c], then:
?- merge_all([a, b, c], [[]], RC).
RC = [[a], [b], [c]].
Given we have this result, we can make the cross product of Bs with this result. For example if Bs = [1, 4], then we obtain:
?- merge_all([a, b, c], [[]], RC), merge_all([1, 4], RC, RB).
RC = [[a], [b], [c]],
RB = [[1, a], [1, b], [1, c], [4, a], [4, b], [4, c]].
With the above generating the cross product of three sets should be straightforward, I leave this as an exercise.

The approach by Daniel Lyons is good in that it allows us to easily control the order of combinations in the cross-product of a list of lists, while keeping the order of elements in the combinations the same, of course:
cross( [], [[]] ).
cross( [XS | T], R ):-
cross( T, TC),
findall( [X | Y], ( % or:
member( Y, TC), % member( X, XS)
member( X, XS) % member( Y, TC),
),
R).
It exhibits good modularity and separation of concerns: the order of presentation is independent of the order of generation and the order of selection.

Related

Is there a way to sum only the integers in a list of pairs that contain a letter and an integer in Prolog?

I'm having trouble figuring out how to find the sum of the integers that are in a list of pairs like so:
[[a, 1], [b, 2], [c, 3], [d, 4]]
I tried something like this, since it is reminiscent of a regular sum function:
sum([], 0).
sum([[_,Head]|[_,Tail]], Sum) :-
sum([_,Tail], Sum2),
Sum is Head+Sum2.
With the call being:
sum([[a, 1], [b, 2], [c, 3], [d, 4]], Total),
write('Sum = '), write(Total).
But that doesn't work. It prints out false, when it should print out the sum, which would be 10 here.
In your attempt to define the predicate sum/2, you're not handling the lists of lists correctly. Try:
sum(Lists, Sum) :-
sum(Lists, 0, Sum).
sum([], Sum, Sum).
sum([[_,N]| Lists], Sum0, Sum) :-
Sum1 is Sum0 + N,
sum(Lists, Sum1, Sum).
This version uses an accumulator to enable a tail-recursive definition. Sample call:
| ?- sum([[a, 1], [b, 2], [c, 3], [d, 4]], Sum).
Sum = 10
yes
I think it might help to split this into two tasks:
create a new list of the second item of each sublist; and
sum up that list.
This makes it easier to tackle the two problems, and furthermore you now have two extra predicates that can be used for other purposes.
We can obtain a list of the second item of the sublists with:
item2list([], []).
item2list([[_, X|_]|T], [X|T2]) :-
item2list(T, T2).
or we can use maplist/3 [swi-doc] and nth1/3 [swi-doc]:
item2list(L1, L2) :-
maplist(nth1(2), L1, L2).
or we can write item2list in terms of findall/3 [swi-doc] and member/2 [swi-doc]:
item2list(L1, L2) :-
findall(X, member([_,X|_], L1), L2).
although here the predicate is not bidirectional.
For example:
?- item2list([[a, 1], [b, 2], [c, 3], [d, 4]], L).
L = [1, 2, 3, 4].
I leave summing up that list as an exercise.
Whenever a goal fails that you expect to succeed, see this as an opportunity to learn (short form for logic earn = earn logic). After all, this is Prolog which was meant to mean Programming in Logic. So where is the logic in your program?
For the moment your program fails, but you expected it to succeed. Where is the culprit? Let's generalize your program such that the resulting program still fails, but is much smaller. There are two easy ways to generalize a program:
remove goals (by adding a prefix *)
remove terms (replacing term by _/*term*/
We can do this pretty blindly. No need to understand your program. Just recheck that the goal still fails. Here is what I came up with on my first try:
:- op(950, fy, *).
* _G_0. % ignore the argument _G_0
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail]], Sum) :-
* sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- sum([_/*[a, 1]*/, _/*[b, 2]*/, _/*[c, 3]*/, _/*[d, 4]*/], Total).
false. % gnah - still fails
One problem has to be in the remaining visible part. Too difficult to figure out? Let Prolog explain it to you by querying the most general query:
?- sum(Xs, Sum).
Xs = []
; Xs = [_A,_B,_C].
So only two lengths of lists are possible: The empty list and a list with three elements. Note that we have currently a generalized version of the predicate. So there is no guarantee that we will find solutions for both lengths. However, we can be 100% sure that for all other lengths there will be no solution.
Let's get back at the original program and ask the most general query:
?- sum(Os, Total).
Os = [], Total = 0
; false.
Oh no, there is a single solution only. And not even a single solution for sum([_|_], Total).
So let's generalize the program again but now with respect to this failing goal:
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail|_/*[]*/]], Sum) :-
sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- Os = [_|_], sum(Os, Total).
false.
In this part there must be a further error. And in fact, the goal sum([_,Tail], Sum2) is the culprit: It is about a list of exactly two elements, but the rule wants at least three
For the actual fixes, see the other answers.
This method works for pure, monotonic programs such as yours.

Add non repeating elements to List in Prolog

I have a List and I am trying to add to it elements from another list that are not already present in the first List.
So if I had 2 Lists :
[a, b, 3, c]
[2, a, b, 4]
The output would be:
[a, b, 3, c, 2, 4]
I am able to get it in reversed order but not in the correct one, here is what I am trying to do:
add_to_list(L, [], L).
add_to_list(List, [H|T], [H|Res]) :-
\+ member(H, List),
add_to_list(List, T, Res).
add_to_list(List, [H|T], Res):-
add_to_list(List, T, Res).
And when I do the method with the 2 Lists mentioned above the output I get is:
[2, 4, a, b, 3, c]
I am aware that my ending clause is adding the L to the end of the result I get, which is why the order is a mess but how can I do it the correct way?
Well the problem here is that you should first move to the end of the first list before concatenating data.
We can still use the code you have written, but alter it slightly like:
atl(_, [], []).
atl(List, [H|T], R) :-
( member(H, List)
-> R = Res
; R = [H|Res]
),
atl(List, T, Res).
We here basically made three changes: (a) we renamed addToList/3 to atl/3; we changed L to [] in the first line; and (c) we used an if-then-else to prevent that the third clause gets triggered even if H is not a member of List (this was a semantical error in your code).
Now we will obtain for the given input as output:
?- atl([a, b, 3, c] , [2, a, b, 4], R).
R = [2, 4] ;
false.
So now we can write an addToList/3 in terms of atl/3: we first generate the list of items to append, and next we use append/3 to append these at the end of the list:
addToList(A, B, L) :-
atl(A, B, R),
append(A, R, L).

Assignment that implements a list in compact form

I have a question in a course assignment.
Consider repetitive lists of the form [a,a,a,b,b,c,a,a,a,a] and their compact form, defined as lists of couples, [[a,3],[b,2],[c,1],[a,4]].
Define the predicate compress/2 such that compress(+L1, ?L2) is satisfied if, given a list L1, L2 is its compact form.
So far, I have come up with the code below:
compress(X,[[X,1]]).
compress([H1,H2|T1],[[H1,C]|T2]):-
H1 = H2,
compress(T1,T2),
C is R + 1.
I am not sure if I am doing it right. Could someone please point to the right direction.
Here are some ideas to get you started.
You're going to need to keep a running count of repeated elements since your results have counters. So right off, consider an auxiliary predicate that includes the counter, which is a typical way of handling it in Prolog. This use of a counter is commonly referred to as an accumulator.
compress(L, C) :-
compress(L, 1, C). % Start counter at 1
Now you'll need to consider a few different cases:
compress([], _, []). % This is an easy one!
This says that if I compress an empty list, I get an empty list.
compress([H], Count, [[H,Count]]). % This is an easy one!
This one says if I compress a list of one element and my current running count is Count, then the result is [[H, Count]].
compress([H, H|T], Count, TC) :-
...
This is the case where I have a running count and the element is still repeating. The result is going to be a list TC but I don't know what it looks like yet since we're still in a repeating cycle and it will need to be determined through recursion. What should this predicate look like? In your example, you included a count in the result when the first two elements were the same, which is not the right time to include the count (see the clause below).
compress([H1, H2|T], Count, [[H1,Count]|TC]) :-
dif(H1, H2),
...
This is the case where I have a running count and the repeating stops at H1. Since the repeating of the current cycle ends with H1, we know the result looks like [[H1, Count]|TC] because H1 has repeated Count times. We just have yet to determine the rest of the list TC through recursion. What should this predicate implementation look like?
There are other ways of doing the above logic (e.g., with -> and ; construct, etc), but this will keep it simple.
Try to think of these as rules where the head of the predicate clause is the assertion which will be true if the following elements of the clause are true. And think recursively.
As an afterthought, this could be done without a separate accumulator by using the result to carry the accumulator:
compress([], []).
compress([H], [[H,1]]).
compress([H1,H2|T], [[H1,1]|R]) :-
dif(H1, H2),
compress(...). % left as an exercise
compress([H,H|T], [[H,N]|R]) :-
N #= N1 + 1,
compress(...). % left as an exercise
I chose to do it this way:
?- compress([a,a,a,b,b,c,a,a,a,a],L), write(L), nl, fail.
compress(X,R) :-
enumerate(X,Y),
collapse(Y,R).
enumerate([],[]).
enumerate([H|T],[[H,1]|R]) :- enumerate(T,R).
collapse([],[]).
collapse([X],[X]).
collapse([[X,N1],[X,N2]|T],R) :- N is N1 + N2, collapse([[X,N]|T],R).
collapse([[X,N1],[Y,N2]|T],[[X,N1]|R]) :- X \= Y, collapse([[Y,N2]|T],R).
The enumerate predicate simply maps [a, a, a, b, b, c, a, a, a, a] to [[a, 1], [a, 1], [a, 1], [b, 1], [b, 1], [c, 1], [a, 1], [a, 1], [a, 1], [a, 1]].
Then I collapse this list down by matching the first two heads of the list - if they unify add the values and try to collapse again. If they don't unify then pop one element off of the list and collapse again. Otherwise there are two base cases - an empty list and a list with one element.
The result is: [[a, 3], [b, 2], [c, 1], [a, 4]].
Here's how you could do it using splitlistIfAdj/3
in combination with dif/3.
First, determine the runs of equal adjacent list items:
?- splitlistIfAdj(dif, [a,a,a,b,b,c,a,a,a,a], Xss).
Xss = [[a,a,a],[b,b],[c],[a,a,a,a]].
Then, map each run to its length using maplist/3 and length/2:
?- maplist(length, [[a,a,a],[b,b],[c],[a,a,a,a]], Ls).
Ls = [3,2,1,4].
Almost done! Let's put it all together using Prolog lambdas:
:- use_module(library(lambda)).
list_compressed(Xs, Yss) :-
splitlistIfAdj(dif, Xs, Xss),
maplist(\Es^(E-N)^(Es=[E|_],length(Es,N)), Xss, Yss).
Sample query:
?- list_compressed([a,a,a,b,b,c,a,a,a,a], Xss).
Xss = [a-3,b-2,c-1,a-4].

Prolog merge two lists

I need to merge two lists in Prolog. On input should be predicate merge/3.
Should work like this:
?- merge([6,4,b,8], [5,b,s,6], X).
X = [6, 4, b, 8, 5, s].
What I have tried:
%rules
merge(A, B, X):-
merge(A, B, B, X).
merge([], X, _, X).
merge([Head|L1], [Head|L2], Tmp, [Head|X]) :-
merge(L1, L2, Tmp, X),
!.
merge(L1, [_|L2], Tmp, X) :-
merge(L1, L2, Tmp, X),
!.
merge([A|L1], [], Tmp, [A|X]) :-
merge(L1, Tmp, Tmp, X),
!.
What I get:
?- merge([1,2,a,3], [5,d,a,1], X).
X = [1, 2, a, 3, 5, d, a, 1].
What I expect:
?- merge([1,2,a,3], [5,d,a,1], X).
X = [1, 2, a, 3, 5, d].
If the order of the elements does not somehow depend on the order of the two input lists, this is an idiomatic Prolog solution:
?- append([6,4,b,8], [5,b,s,6], A), sort(A, B).
A = [6, 4, b, 8, 5, b, s, 6],
B = [4, 5, 6, 8, b, s].
If the order is important, you need to explain how exactly.
And some comments on the code you show. The names that you have chosen for your predicates: both "join" and "merge" have well-established meanings different from what you seem to be attempting to achieve ("join" as in relational databases, "merge" as in "merge two ordered lists"). What you are doing is rather a "union" (and by the way, click on this link and read the code!).
Also, it is almost always a mistake (not an error, but a mistake) to have a cut as the last subgoal of a clause body. Having multiple clauses to a predicate that are not obviously mutually exclusive (as the last 3 of the 4 clauses to your merge/4) is commonly a design flaw (not a mistake).
This can be done by rewriting built-in predicates ! e.g :
my_append([], R, R) .
my_append([H|T], R1, [H|R2]) :-
my_append(T, R1, R2).
my_member(H, [H|_]).
my_member(H, [_|T]) :-
my_member(H, T).
So, I can say that merging L with an empty list gives this list L
merge(L, [], L).
Now, to merge two lists, I look at the first element of the second list.
If it is in the first list, I ignore it and I merge the first list, with the rest of the second.
If not, I add this first element at the end of the first list and I merge the new first list with the rest of the second.
I must say that it's not very efficient !!
merge(L, [H| T], R) :-
( my_member(H, L)
-> merge(L, T, R)
; my_append(L, [H], L1),
merge(L1, T, R)).

How to remove a list from a list in prolog?

I want to implement the following problem in prolog:
Given
L1=[1,2,3,4] and L2=[2,3,4]
calling a function named remove_list(L1,L2,L) will remove L2 from L1 .
So L will be [1].
But if the elements of the 2nd list are not in the same order as in L1 or more accurately the 2nd one is not a subset of the first List ,it wont remove anything.
SayL1=[1,2,3,4,5] and L2=[2,3,6] or L2=[2,6] or L2=[4,3,2] will result L=[1,2,3,4,5]
Any help will be highly appreciated.
Thanks in advance
You can build your predicate remove_list/3 using recursivity, which is an useful tool when dealing with lists in Prolog.
remove_list([], _, []).
remove_list([X|Tail], L2, Result):- member(X, L2), !, remove_list(Tail, L2, Result).
remove_list([X|Tail], L2, [X|Result]):- remove_list(Tail, L2, Result).
Consult:
?- remove_list([4,5,1,6,3], [1,4,7], L).
L = [5, 6, 3].
The idea is to copy every element in your original list "L1" to your final list "L", except when that element is member of the second list "L2".
Your base clause is your stop condition, when your original list "L1" is empty, in that case ignoring your list "L2", the result is always the same empty list. (You can't delete nothing from the empty list).
Your second clause, don't copy the element in the head of the list "L1" to the final list "L" if that element in the head is member of the list "L2", also make the recursive call to the predicate with the Tail of your list "L".
And the final clause, copy the element in the head of the list "L1" to the final list "L", and also make the recursive call to the predicate with the Tail of that list "L". We don't need the goal member/2 here, because we used a cut in the previous clause.
EDIT: This answer should only be considered if you want to remove items from the list "L1" contained in the "L2" list, regardless of the order. To remove the subset "L2" from set "L1", please use Lurker's solution or this other solution:
remove_list(L, [], L):- !.
remove_list([X|Tail], [X|Rest], Result):- !, remove_list(Tail, Rest, Result).
remove_list([X|Tail], L2, [X|Result]):- remove_list(Tail, L2, Result).
This new solution considers the order of the elements in list "L2", but not in the strict sense, ie, may be interspersed in the original list "L1", which does not violate the concept of "L2" being a subset of "L1".
[2,4] is a subset of the set [1,2,3,4,5,6], but [2,4,7] is not:
?- remove_list([1,2,3,4,5,6], [2,4], L).
L = [1, 3, 5, 6].
?- remove_list([1,2,3,4,5,6], [4,2], L).
false.
?- remove_list([1,2,3,4,5,6], [2,4,7], L).
false.
Now, given the fact that is desired to obtain the original set rather than the negative response in case that any of the elements from the original set can be removed, then we use an auxiliary predicate:
rm_subset(L1, L2, L):- remove_list(L1, L2, L),!.
rm_subset(L1, L2, L1).
Consult:
?- rm_subset([1,2,3,4,5,6], [4,2], L).
L = [1, 2, 3, 4, 5, 6].
?- rm_subset([1,2,3,4,5,6], [2,4], L).
L = [1, 3, 5, 6].
Another possible solution, using the delete/3 predicate:
remove_elements(L, [H|T], R) :-
delete(L, H, R1),
remove_elements(R1, T, R).
remove_elements(L, [], L).
| ?- remove_elements([4,5,1,6,3], [1,4,7], L).
L = [5,6,3] ? ;
no
| ?-
EDIT I just realized I completely misread the problem. Duh. If you want to maintain order of the "removed" list as stated, then Boris' comment is right on regarding append/3. append(A, B, C) means that if you take A and append B you get C, with element order preserved.
So, to restate the solution as requested:
remove_elements(L1, L2, L) :-
append(A, B, L1),
append(C, L2, A),
append(C, B, L).
In general, if you just want to delete multiple elements in a list from another list you can just use:
subtract(+Set, +Delete, -Result)
where set = unorderd list which can have some duplicated elements and
delete = list of element we want to delete from Set.
Ex:
subtract([1,3,5,6,4,2,3], [1,2,3], R).
R = [5, 6, 4].