I'm new to Prolog and have a question:
Suppose I have a list of lists; [list1, list2, list3,..., list_n]
If list_j contains list_i, i.e. every variable in list_i occurs in list_j, then remove list_j.
For example, if the input is
[[a,b,c], [a,c], [a,d], [a,d,e]]
the output should be
[[a,c], [a,d]]
because [a,b,c] contains [a,c] and [a,d,e] contains [a,d].
How to implement this in SWI-Prolog? Any help is appreciated.
First of all, SWI already has a predicate to check whether a list is "contained" (in terms of set-inclusion) in another list: subset/2 (http://www.swi-prolog.org/pldoc/man?predicate=subset/2).
Now you can use that to check whether or not to remove a specific list from all lists:
remove(Lists, List) :-
member(List, Lists),
member(Sublist, Lists),
Sublist \= List,
subset(Sublist, List).
Read as: remove List from Lists if it is a member of Lists and there is also another (confirmed by \=) member of Lists (call it Sublist), which is a subset of List.
?- remove([[a,b,c], [a,c], [a,d], [a,d,e]], L).
L = [a, b, c] ;
L = [a, d, e] ;
Now you can use that to answer your original question:
remaining(Lists, Remaining) :-
bagof(List,
(
member(List, Lists),
\+ remove(Lists, List)
),
Remaining).
Let these lists be Remaining from Lists which are members of the original list of Lists and are not (\+) to be removed.
?- remaining([[a,b,c], [a,c], [a,d], [a,d,e]], Remaining).
Remaining = [[a, c], [a, d]].
Related
Write a predicate triangle(Bs, Ds) where Bs a list of the positions of the foo and Ds is the (single) list of differences in position. Use the built-in predicate append and the your own distances predicate.
This is related to this questions :
Build a list with abs() in prolog
distances([], _, []).
distances([H|T], B, [D|Ds]) :- abs(H - B, D), distances(T, B, Ds).
triangle([],[]).
triangle([H|T], [D|Dt]) :- distances(T,H,D), triangle(T,Dt).
?- triangle([1,2,3],A).
A = [[1, 2], [1], []].
The Solution I require
?- triangle([1,2,3],A).
A = [1,2,1].
The answer is correct but it is a in list of lists.
I'm having trouble turning Ds into a single list. I have tried using append in various positions within the predicate but either get repetitions or the predicate evaluates to false. How can I turn Ds into a single list [1,2,3] with append?
You can append the list D with Dt, instead of using [D|Dt] where you thus prepend the list with one element D:
triangle([],[]).
triangle([H|T], Ds) :-
distances(T, H, D),
append(D, Dt, Ds),
triangle(T, Dt).
let's say that I have some clauses, two of which are the following:
has_basket('Sarah', 'bigBasket','apple','grape').
has_basket('Sarah', 'bigBasket','orange','berry').
How can I write a rule named
all_baskets(S,L).
which will give me the list of all the baskets that person S has in the following form?
for example
all_baskets('Sarah',L).
will give us:
L=[['bigBasket','apple','grape], ['bigBasket','orange','berry']]
Whenever you consider a Prolog programming problem, you need to clarify the aspects that are important to you.
Think of the order of the two elements in the list. Are they in that order because they are in alphabetical ascending order, or is it just luck?
What do you expect for all_baskets('Nemo', L)? Should this fail, or rather answer L = []?
What do you expect for all_baskets(P, [])?
If you say 1 alphabetical, 2 fail, and 3 fail, then setof/3 is the way to go.
has_basket('Sarah', bigBasket, apple, grape).
has_basket('Sarah', bigBasket, orange, berry).
has_basket('Bernie', bigBasket, orange, apple). % another fact
all_baskets(P, L) :-
setof([A, B, C], has_basket(P, A, B, C), L).
?- all_baskets(P, []).
false.
?- all_baskets('Nemo', L).
false.
?- all_baskets(P, L).
P = 'Bernie', L = [[bigBasket,orange,apple]]
; P = 'Sarah', L = [[bigBasket,apple,grape],[bigBasket,orange,berry]].
We can make use of the findall/3 predicate [swi-doc]:
all_baskets(S,L) :-
findall([A, B, C], has_basket(S, A, B, C), L).
Here [A, B, C] is the "template" of the items in the list, has_basket(S, A, B, C) the "goal" that needs be fullfilled, and L the list of results.
I m studying Prolog and i see this code
foo([],[]).
foo([[A,_ ]|L], [A|P]) :-foo(L ,P).
The result say that this code take N element of list of list,
Ad example if we give this query:
?foo([[car],[house],[man]],X)
X= [c,h,m]
At first read i see that something wrong.
For me this code take the tail of list of list and the rest of first element of the list , so for me first expansion will be (trace)
foo([[house],[man]], ar)
foo([[man]], ouse)
foo([], an)
false.
I try to compile with swi-prolog and give this trace:
[trace] ?- trace,foo([[car],[house],[man]],X).
Call: (9) foo([[car], [house], [man]], _1016) ? creep
Fail: (9) foo([[car], [house], [man]], _1016) ? creep
false.
What are I wrong?
Obtaining the first elements
The pattern [A, _] in your clause is wrong, or at at least not generic enough. [A, _] unifies with a list that contains exactly two elements, but this will thus fail for lists with more than two elements, or with one elements, like you found out.
You need to use the [A|_] pattern: indeed a list where the head is A, and we are not interested in the rest (tail). like:
foo([],[]).
foo([[A|_]|L], [A|P]) :- foo(L, P).
That being said, you can simplify this, by implementing a predicate that takes the head of a list:
head([H|_], H).
and then make use of maplist/3 [swi-doc]:
foo(A, B) :-
maplist(head, A, B).
maplist will thus call head like head(Ai, Bi), with Ai and Bi elements of A and B respectively.
Obtaining a substring with the first character
but based on the sample output, this is not what you want: you also want to obtain the first "character" of the atom, we can do that by using string_chars/2 [swi-doc]:
head_first([A|_], C) :-
string_chars(A, [C|_]).
and then define foo/2 again with maplist/3 [swi-doc]:
foo(A, B) :-
maplist(head_first, A, B).
we then obtain:
?- foo([[car],[house],[man]], X).
X = [c, h, m].
Can anyone explain how this member rule works using concatenation? I figured concat just returns a new list rather than seeing if a element is in the list. Concat rule is taken from my textbook.
concat([ ], L, L).
concat([H|T], L, [H|M]) :- concat(T, L, M).
member(X, L) :- concat(L1, [X | L2], L).
ex) member(a, [a, b, c]) => True
Prolog predicates usually work in different ways. Concat can be used to create a list, but it can also be used to split one list into two. One way to read the call to concat in this case is
Are there lists L1 and [X|L2] such that their concatenation is L?
Because X is the head of the second list, you know that if this statement is true, then X is a member of L.
In the example, L1 would unify with [] (i.e. all elements before a) an L2 would unify with [b, c] (all elements after a).
I have a list and a list of lists:
A = [1,2,4,5]
L = [[1,2,5],[3,4,5]]
If A contains the same elements as one of the lists, I want it to return true. As A contains the same elements (1,2,5) as the first list in L ([1,2,5]), it should return true even though there's one element in A that isn't in the first list in L.
I've tried using a couple of predicates supplied in the answer of a similar question in order to solve this:
p(X):- findall( Y, (member(Y,X), \+ have_common_element(X,Y) ), [_]).
have_common_element(A,B):- member(X,A), memberchk(X,B).
However the following query will return false:
p([[[1,2,5],[3,4,5]],[1,2,4,5]]).
I understand that this is because there is an element in A (4) that isn't in the first list of L, although I'm having difficulty figuring out how to extend the predicates to have the query return true.
Would it be possible to extend these predicates so that true will be returned even with the additional (and non-mutual) element included?
What you want to say seems to be:
p(A, Ess) :-
member(Es, Ess), % there is a list Es in Ess
maplist(A+\E^member(E,A), Es). % for all E in Es: member(E,A).
or without lambdas:
p(A, Ess) :-
member(Es, Ess),
maplist(list_member(A), Es).
list_member(L, E) :-
member(E, L).