Unifying list of lists prolog - list

I googled this but cant find the answer, so here you go:
I have this function in prolog:
ing(Lis) :- findall(I,( recipe2(_,ingredients(I,_)) ),Lis).
This function search and returns me a list of lists like this:
L = [['wheat flour', egg, salt], ['wheat flour', cheese, olives, tomato, salt, basil], ['wheat flour', potatoes, salt], [milk, egg, sugar]].
I want to unify that list of lists in only one list, so i can get out duplicates. I know i have to use recursion, but thats all i know.
Thanks in advance.

You may simply modify the predicate like such:
ing(Lis) :-
setof(E, X^Y^I^( recipe2(X, ingredients(I,Y)), member(E, I) ), Lis).
member/2 is a built-in predicate that unifies the first argument with an element of a list in the second argument. It is non-deterministic.
The use of X^Y^I^ are existential quantifiers to ensure that you only get your results in one solution. It essentially says,
There exists an X, Y, and I for any element E that is a part of an
ingredient list, (I).
Using setof/3 also ensures that any solution you get will be a collection of unique elements.
Documentation (SWI-Prolog) for member/2 and setof/3

Related

Counting how many elements in a list of lists satisfy a predicate

Given a list of lists of integers, e.g. [[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]], I want to go over each sublist and count how many of them sum to 15. In this case that would be 1, for the sublist [3,10,2].
I am aware of the predicate aggregate_all/3, but I'm having trouble writing a predicate to check each element of the list, what I have now is something like
fifteens([X|Xs]) :-
sum_list(X, 15),
fifteens(Xs).
and within another predicate I have:
aggregate_all(count, fifteens(Combinations), Value).
where Combinations is the list of lists of integers in question.
I know my fifteens predicate is flawed since it's saying that all elements of the nested list must sum to 15, but to fix this how do I take out each element of Combinations and check those individually? Do I even need to? Thanks.
First of all your fifteens/2 predicate has no because for empty list and thus it will always fails because due to the recursion eventually fifteens([]) will be called and fail.
Also you need to change completely the definition of fifteens, currently even if you add base case, it says check ALL elements-sublists to see if they sum to 15. That's Ok but I don't see how you could use it with aggregate.
To use aggregate/3 you need to express with fifteens/2, something like: for every part of my combinations list check separately each sublist i.e each member:
ifteens(L) :-
member(X,L),
sum_list(X, 15).
Now trying:
?- aggregate_all(count, ifteens([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]]), Value).
Value = 1.
This is a job for ... foldl/4. Functional programming idioms in logic programming languages? Yes, we can!
First, summing the summable values of a list:
sum_them(List,Sum) :-
foldl(sum_goal,List,0,Sum).
sum_goal(Element,FromLeft,ToRight) :-
must_be(number,Element),
must_be(number,FromLeft),
ToRight is Element+FromLeft.
Then, counting the ones that sum to 15:
count_them(List,Count) :-
foldl(count_goal,List,0,Count).
count_goal(Element,FromLeft,ToRight) :-
must_be(list(number),Element),
must_be(number,FromLeft),
sum_them(Element,15) -> succ(FromLeft,ToRight) ; FromLeft = ToRight.
Does it work? Let's write some unit tests:
:- begin_tests(fifteen_with_foldl).
test("first test",true(R==1)) :-
count_them([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]],R).
test("test on empty",true(R==0)) :-
count_them([],R).
test("test with 2 hist",true(R==2)) :-
count_them([[15],[],[1,1,1,1,1,10]],R).
:- end_tests(fifteen_with_foldl).
And so:
% PL-Unit: fifteen_with_foldl ... done
% All 3 tests passed
true.

Reverse every second list of lists in Prolog

I have a list containing lists and I want to reverse every second list in it. I tried something but if I have odd number of elements in the list the last list element is lost... So the best solution would be to put the odd lists first and the even lists second till every second list is reversed.
I can't use any libraries. I need to do it recursively or split them and append them again. The best thing I made so far was to reverse only the first even list and append the first odd and even list in a new list.
I tried to do this:
reverselist(List, [List]).
reverselist([X,Y|Rest], [SnakeList|Rest2]):-
append(X, [], Odd),
reverse(Y, EvenList),
append(Odd, EvenList, SnakeList),
reverselist(Rest, Rest2).
And this:
reverselist(List1, List2).
reverselist([H|Ts], [Odd|R]):-
not(0 is H mod 2),
append(H, [], Odd),
reverselist(Ts, R).
reverselist([H|Ts], [Even|R]):-
0 is H mod 2,
reverse(H, Even),
reverselist(Ts, R).
Sample query:
?- reverselist([[a,b,c],[d,a,b],[c,d,o],[b,c,d],[e,e,d]], List).
I want the result to be:
List = [ [a,b,c],[b,a,d],[c,d,o],[d,c,b],[e,e,d] ].
You can also write mutual recursion:
reverselist([],[]).
reverselist([H|T],[H|T1]):-reverselist2(T,T1).
reverselist2([],[]).
reverselist2([H|T],[H1|T1]):-reverse(H,H1), reverselist(T,T1).
You were pretty close with your first variant.
Instead of your
reverselist(List, [List]).
reverselist([X,Y|Rest], [SnakeList|Rest2]):-
append(X, [], Odd),
reverse(Y, EvenList),
append(Odd, EvenList, SnakeList),
reverselist(Rest, Rest2).
just tweak it as
reverselist([], []). % additional clause
reverselist([List], [List]).
reverselist([X,Y|Rest], [X,EvenList|Rest2]):-
reverse( Y, EvenList),
reverselist( Rest, Rest2).
All three clauses are mutually exclusive and together they are exhaustive, i.e. they cover every possibility.
I believe this definition to be the most immediate and close representation of your problem. In Prolog, to formulate the problem means to have the solution for it.
We need to create another predicate with one more argument to keep track of odd or even position:
reverselist(InList,OutList):- reverselist(InList,OutList, 0).
reverselist([],[],_). %base case
%case of even position
reverselist([H|T],[H|T1], 0):- reverselist(T,T1,1).
%case of odd position
reverselist([H|T],[H1|T1], 1):- reverse(H1,H), reverselist(T,T1,0).

Best way to remove the first elements from each list in a list of lists in Prolog?

I am trying to remove the first element of every list in a list of lists.
For example, to list [[1,2],[3,4]], I should return [[2],[4]].
In most situations, this code below will work fine:
remove_firstElem([],[]).
remove_firstElem([[_H|T]|Ls],[T|L]) :-
remove_firstElem(Ls,L).
But for lists like [[1],[2]], I would like it to return [] rather than [[],[]].
What I tried so far looks like:
remove_firstElem([_H|Ls],L) :-
length(_H,1),
remove_firstElem(Ls,L).
But it returns [ ],[[ ]],[[ ]],[[ ],[ ]] and I really don't know what's wrong with it.
Can anyone help me to fix it? Thanks for any help!
If I understand it correctly, you want to pop the head of the list, but in case the list contains only one element (or none at all), that list should be removed.
We can check if the sublist contains at least two elements with the pattern:
pop_lists([[_,H2|T]|TA],[[H2|T]|TB]) :-
pop_lists(TA,TB).
so here we have a pattern [_,H2|T] for the first list. The _ binds with the first element, H2 with the second element, and the remaining elements with the tail.
Lists that can not unify with that pattern are the empty list, or a list with one element. So in that case we simply ignore them:
pop_lists([[]|TA],TB) :-
pop_lists(TA,TB).
pop_lists([[_]|TA],TB) :-
pop_lists(TA,TB).
In case we reach the end of the list, of course we unify the filter with the empty list as well:
pop_list([],[]).
we better put this clause on the first line to make our predicate more multidirectional. So in full, we have the following solution:
pop_list([],[]).
pop_list([[_,H2|T]|TA],[[H2|T]|TB]) :-
pop_list(TA,TB).
pop_list([[]|TA],TB) :-
pop_list(TA,TB).
pop_list([[_]|TA],TB) :-
pop_list(TA,TB).
We can further reorder the statements, such that the amount of backtracking is less:
pop_list([],[]).
pop_list([[]|TA],TB) :-
pop_list(TA,TB).
pop_list([[_]|TA],TB) :-
pop_list(TA,TB).
pop_list([[_,H2|T]|TA],[[H2|T]|TB]) :-
pop_list(TA,TB).
Easier way:
list_tail([_|Es], Es).
maplist(list_tail, Input, Output).

Prolog: Sort a list by alternative index

I'm attempting to sort a list of colors, by a given preffered order. For example a list [r,z,z,w,g,g,r,z] sorted in this order [z,b,g,r,w], will give an end result of [z,z,z,g,g,r,r,w].
I tried using a basic bubblesort algorithme and adding a check to see which of first two terms would be 'higher' on the order list.
% take the to-sorted list, the order in which to sort the list, and the
% result.
%colourSort([r,z,z,w,g,g,r,z],[z,b,g,r,w],X). returns X = [z,z,z,g,g,r,r,w]
colourSort(List,Order,Sorted):-
swap(List,List1,Order),
!,
colourSort(List1,Order,Sorted).
colourSort(Sorted,_,Sorted).
% check if the either the first or second letter is first in the order
% list, if neither check the next letter in the order list.
check(A,_,[H|_],A):-
A == H.
check(_,B,[H|_],B):-
B == H.
check(A,B,[_|T],R):-
check(A,B,T,R).
check(_,_,[],_).
%swap incase a set of letters isn't ordered, continues otherwise.
swap([X,Y|Rest],[Y,X|Rest],Order):-
check(X,Y,Order,R),
X == R.
swap([Z|Rest],[Z|Rest1],Order) :-
swap(Rest,Rest1,Order).
When I run the code, it ends up crashing my swi-prolog, I'm assuming it's getting stuck in a loop or something, but haven't been able to figure out why or how. Any advice or tips would be appreciated.
Here's a solution to the stated problem, which does not, however, use a custom sorting algorithm. Instead, it uses the common pairs data-structure (using the (-)/2 operator to form a list of items Key-Value) and the keysort/2 for sorting. Edit: this answer has been reworked in accordance with #mat's tip in the comments, and to provide a more succinct explanation).
Solution:
item_with_rank(Ranking, Item, Rank-Item) :-
nth0(Rank, Ranking, Item).
sort_by_ranking(Ranking, ToSort, Sorted) :-
maplist(item_with_rank(Ranking), ToSort, Ranked),
keysort(Ranked, RankedSorted),
pairs_values(RankedSorted, Sorted).
Explanation:
We define a predicate item_with_rank(Ranking, Item, Rank-Item) that uses a list of arbitrarily ordered terms as a Ranking, and associates with the given Item a Rank which is equivalent to the 0-based index of the first term in Ranking that unifies with Item. We then define sort_by_ranking(Ranking, ToSort, Sorted). sort_by_ranking/3 uses maplist/3 to call item_with_rank/3, with the given Ranking, on each element of the list ToSort, obtaining a list of pairs, Ranked, assigning a rank to each item. We use keysort/2 to sort the Ranked so that they order of elements accords with the value of their "ranks" (keys) in RankedSorted. When we extract just the values from RankedSorted, we are left with the Sorted items, which is what we were after:
Example of usage:
?- sort_by_ranking([z,b,g,r,w], [r,z,z,w,g,g,r,z], S).
S = [z, z, z, g, g, r, r, w] ;
false.

Prolog return value comparation

I'm traying to learn the basics of logic programming.
I solved some exercises, and now I'm having trouble on creating a function that take two arguments, a list of non empty lists whose elements concatenated together form the second argument.
By the time I created a function that concat the elements of a list of lists:
concat([[]|L],L3):- concat(L,L3).
concat([[Head|L1]|L2],[Head|L3]):- concat([L1|L2],L3),!.
Now, what I need to know is how to take the return value of that function and compare it with a list (the second argument of the function).
This is one approach that will allow down to single elements in a sublist, but not an empty sublist:
concat([[L]], [L]).
concat([[H],L|T], [H|R]) :- concat([L|T], R).
concat([[H1,H2|T]|LT], [H1|RT]) :- concat([[H2|T]|LT], RT).
The method here to avoid an empty list is to call out two head elements in the recursive clause, and a solitary element list in the base case. This prevents empty sublists from succeeding, as requested in the comments of the original post.
If you have a variable, Y that is already instantiated, and you want to know if it is the result of concatenating the list of lists, LL, you simply query:
concat(LL, Y).
This will be true if Y is the concatenation of list LL and false if it is not. You don't have to "return and compare" (as, for example, in C, you might say, concat(LL) == Y, or concat(LL, X); if (X == Y)...). This is because concat is a relation defined between the two arguments and it determine if the query can be made true by following the stated rules (clauses of the predicate).
If you already obtained a result and want to determine if it's unifiable to another variable, Z, then you can say:
concat(X, Y), Y = Z.
Note that, in Prolog, concat(X, Y) == Z is not correct to determine if the result of the predicate is equal to Z because it is not a function that returns a value.
Prolog doesn't have functions or return values in the sense of a procedural programming language. It has predicates, which assert a relationship. It has terms, which include variables which comes with some strictures:
All variables are local, and
A variable, once assigned a value, ceases to be variable. That's why it's called unification.
So....
If you want a predicate that will take two lists and produce their concatenation, you'll need to pass it a 3rd variable. You might invoke it like this:
concat([a,b],[c,d],X).
which asserts that X is the concatenation of [a,b] and [c,d]. Prolog's inference engine will then evaluate the truth or falseness of the assertion.
Most recursive problems has a few special cases and a more general case. The implementation of such a concat/3 predicate might look something like this (annotated to explain what it's doing).
First, we have one special (and terminating) case: If the left-hand list is empty, the concatenation is simply the right-hand list.
concat( [] , Bs , Bs ).
Next, we have the one general case: if the left-hand list is non-empty, we need to prepend it to the concatentation that we're building (and then recurse down.)
concat( [A|As] , Bs , [A|Cs] ) :-
concat(As,Bs,Cs).
That's all there is two it. You'll also notice that it's bi-directional: it's perfectly happy to split lists apart as well. Invoking it like this:
concat( Prefix , Suffix, [a,b,c,d] ).
will, on backtracking, produce all the possible ways that [a,b,c,d] could be split into a prefix and suffix:
Prefix Suffix
--------- ---------
[] [a,b,c,d]
[a] [b,c,d]
[a,b] [c,d]
[a,b,c] [d]
[a,b,c,d] []
You just need the base case
concat([],[]).
concat([[]|L],L3):- concat(L,L3).
concat([[Head|L1]|L2],[Head|L3]):- concat([L1|L2],L3).
It also works for empty (sub)lists.
I've removed the useless cut.