Related
I want to create a predicate filter/3 or filter(Condition,List,Solution) that checks every element of the list and if returns true in the condition put it in the Solution list. How can I do this?
This is my code:
%% less_ten/1
%% less_ten(X)
less_ten(X) :- X < 10.
%% less_twenty/1
%% less_twenty(X)
less_twenty(X) :- X < 20.
%% filter/3
%% filter(C,List,Solution)
filter(_,[],Solution).
filter(C,[Head|Tail],Solution) :-
(
Predicate=..[C,Head],
call(Predicate),
!,
append(Solution,Head,NewSolution),
filter(C,Tail,NewSolution)
);
(
filter(C,Tail,Solution)
).
Seems like all you need is
filter( [] , _ , [] ) .
filter( [X|Xs] , P , [X|Ys] ) :- call(P,X), !, filter(Xs,P,Ys) .
filter( [_|Xs] , P , Ys ) :- filter(Xs,P,Ys) .
The reason for putting the source list before the test predicate is that first argument indexing can improve execution speed.
I have two lists with song names and the singer - top 10 for EU and top 10 for US. The result should be a list containing the songs which exists in both lists.
Here are the predicates which sets the lists:
top10us([
song(all_About_That_Bass, "Meghan Trainor"),
song(shake_It_Off, "Taylor Swift"),
song(black_Widow, "Iggy Azalea Featuring Rita Ora"),
song(bang_Bang, "Jessie J, Ariana Grande & Nicki Minaj"),
song(anaconda, "Nicki Minaj"),
song(habits, "Tove Lo"),
song(dont_Tell_Em, "Jeremih Featuring YG"),
song(animals, "Maroon 5"),
song(stay_With_Me, "Sam Smith"),
song(break_Free, "Ariana Grande Featuring Zedd")
]).
top10eu([
song(prayer_In_C, "Lilly Wood & Prick"),
song(lovers_On_The_Sun, "David Guetta & Sam Martin"),
song(chandelier, "Sia"),
song(rude, "Magic!"),
song(stay_With_Me, "Sam Smith"),
song(maps, "Maroon 5"),
song(all_Of_Me, "John Legend"),
song(all_About_That_Bass, "Meghan Trainor"),
song(a_Sky_Full_Of_Stars, "Coldplay"),
song(bailando, "Enrique Iglesias")
]).
Here are my two attempts to write a predicate doing the job.
Attempt 1:
member(ITEM,[song(ITEM,_)|_],1).
member(_,[],0).
member(ITEM,[_|T],R):-
member(ITEM,T,R).
both([],[],[]).
both([song(NAME,_)|T1], L2,[NAME|T3]):-
member(NAME,L2,R),R=1,both(T1, L2, T3).
both([_|T], L2, T3):-
both(T, L2, T3).
?- top10eu(L1),top10us(L2),both(L1,L2,RESULT),write(RESULT). // the result is false here
Attempt 2:
both(_,[],[],[]).
both(ORIGINAL,[_|T],[],OUTPUT):-
both(ORIGINAL,T,ORIGINAL,OUTPUT).
both(ORIGINAL, [song(NAME,SINGER)|T1], [song(NAME,_)|T2],[NAME|T3]):-
both(ORIGINAL, [song(NAME,SINGER)|T1], T2, T3).
both(ORIGINAL, L1, [_|T2], T3):-
both(ORIGINAL, L1, T2, T3).
?- top10eu(L1),top10us(L2),both(L1,L1,L2,RESULT),write(RESULT). // here a list is returned, but with wrong elements.
I'm hanging my head in wall for hours and hours and can't solve this issue. Could someone more into prolog take a look? I believe this is not too concrete, as the main question is how to get values existing in two lists.
EDIT: My 2nd try happens to be working, but I was calling it with bad parameter list. Calling it with ?- top10eu(L1),top10us(L2),both(L2,L1,L2,RESULT),write(RESULT). returns the correct list: RESULT = [stay_With_Me, all_About_That_Bass]
Anyway, is there more elegant way of doing this?
a join between two unsorted lists is easily obtained with the help of library predicates:
both(L1,L2,L) :-
findall(E, (member(E, L1), memberchk(E, L2)), L).
to extract only part of the matched structures, or to perform more elaborate matching, we can refine the goal:
bothSong(L1,L2,L) :-
findall(Song, (E = song(Song, _), member(E, L1), memberchk(E, L2)), L).
note that only songs having the same author will be retrieved. This because the variable E will bind to song(Title, Author).
To find the intersection two lists, you could say say something like this:
intersection( [] , _ , [] ) . % once the source list is exhausted, we're done.
intersection( [X|Xs] , Ys , [X|Zs] ) :- % otherwise, add X to the results...
member(X,Ys) % - if X is found in Y (built-in predicate)
! , % - cut off alternatives
intersection(Xs,Ys,Zs) % - recurse down
. %
intersection( [_|Xs] , Ys , Zs ) :- % otherwise (X not in Y), discard X
intersection(Xs,Ys,Zs) % - and recurse down
. % Easy!
If you're not allowed to use the built-in member/2, it can be pretty trivial so just roll your own:
member(X,[X|_]) :- ! .
member(X,[_|L]) :- member(X,L) .
But from your post, it looks like you might want to do a little remodelling of your data structure, along these lines, making it a bunch of facts:
top10( us , all_About_That_Bass , "Meghan Trainor" ).
top10( us , shake_It_Off , "Taylor Swift" ).
top10( us , black_Widow , "Iggy Azalea Featuring Rita Ora" ).
top10( us , bang_Bang , "Jessie J, Ariana Grande & Nicki Minaj" ).
top10( us , anaconda , "Nicki Minaj" ).
top10( us , habits , "Tove Lo" ).
top10( us , dont_Tell_Em , "Jeremih Featuring YG" ).
top10( us , animals , "Maroon 5" ).
top10( us , stay_With_Me , "Sam Smith" ).
top10( us , break_Free , "Ariana Grande Featuring Zedd" ).
top10( eu , prayer_In_C , "Lilly Wood & Prick" ).
top10( eu , lovers_On_The_Sun , "David Guetta & Sam Martin" ).
top10( eu , chandelier , "Sia" ).
top10( eu , rude , "Magic!" ).
top10( eu , stay_With_Me , "Sam Smith" ).
top10( eu , maps , "Maroon 5" ).
top10( eu , all_Of_Me , "John Legend" ).
top10( eu , all_About_That_Bass , "Meghan Trainor" ).
top10( eu , a_Sky_Full_Of_Stars , "Coldplay" ).
top10( eu , bailando , "Enrique Iglesias" ).
Then you could just say something like:
both( Country1 , Country2 , Common ) :-
findall(
song(Title,Performer) ,
( top10(Country1,Title,Performer) ,
top10(Country2,Title,Performer)
) ,
Common
) .
And if you just wanted the song names:
both( Country1 , Country2 , Common ) :-
findall(
Title ,
( top10(Country1,Title,_) ,
top10(Country2,Title,_)
) ,
Common
) .
What you are looking for is commonly called set intersection. Check out the related question "Intersection and union of 2 lists".
All code presented in other answers is logically impure.
Logically impure code often breaks when it is used in a slightly different way than the implementer intented.
The code I presented in my answer to above question OTOH is logically pure and monotone. It thus remains logically sound even when non-ground terms are being used.
I'm trying to make intersection of 2 lists (i.e. list C contains those and only those elements, that are in A and B), yet as I understand, I get disjunction of 2 lists + any amount of any elements in C.
Intended to work like:
if X is in C, then it must be both in A and in B. (I believe X should iterate ALL members of C !?)
predicate: d(A,B,C) :- (member(X,D)->member(X,A),member(X,B)).
Can you tell: Are my sentence and predicate not equal or did I make another error?
example:
?- [user].
|: d(A,B,C) :- (member(X,D)->(member(X,A),member(X,B))).
|: % user://1 compiled 0.01 sec, 612 bytes
true.
?- d([a,b],[b,c],C)
| .
C = [b|_G21] .
?- d([a,b],[b,c],[b]).
true .
A O(NlogN) solution with duplicates removed:
% untested
intersection(A, B, O) :-
sort(A, AS),
sort(B, BS),
intersection1(AS, BS, O).
intersection1(A, B, O) :-
( A = [AH|AT],
B = [BH|BT]
-> ( AH == BH
-> O = [AH|OT],
intersection1(AT, BT, OT)
; ( AH #< BH
-> intersection1(AT, B, O)
; intersection1(A, BT, O) ) )
; O = [] ).
I like the solution proposed by #salva, though I'd do a more straightforward sort-and-merge, chucking anything that doesn't match instead:
intersect( As , Bs , Cs ) :-
sort( As , SortedAs ) ,
sort( Bs , SortedBs ) ,
merge( SortedAs , SortedBs , Cs )
.
merge( [] , [] , [] ).
merge( [] , [_|_] , [] ).
merge( [_|_] , [] , [] ).
merge( [C|As] , [C|Bs] , [C|Cs] ) :- merge( As , Bs , Cs ) .
merge( [A|As] , [B|Bs] , Cs ) :- A #< B , merge( As , [B|Bs] , Cs ) .
merge( [A|As] , [B|Bs] , Cs ) :- A #> B , merge( [A|As] , Bs , Cs ) .
your predicate d/3 should be reformulated in constructive terms, since Prolog it's 'a tuple at once' relational language:
d(X,Y,Z) :- findall(E, (member(E,X), memberchk(E,Y)), Z).
that yields
?- d([a,b],[b,c],C).
C = [b].
memberchk/2 it's the deterministic version of member/2, used here to enumerate all X' elements. You could understand better the difference if you replace memberchk with member and try to call d/3 with lists containing duplicates.
findall/3 it's the simpler 'all solutions' list constructor.
I have a list:
L = [1,2,3,4,5,6,7,8]
I would like to transform it into this:
L= [ex(1,2),ex(3,4),ex(5,6),ex(7,8)]
How can I effectively do this?
[edited to add:]
This is what I have so far:
convert( [] , S ) .
convert( [A,B|Rest] , S ) :-
S is ( ex(A,B) | Rest ) ,
convert(Rest)
.
Gosh, have you tried anything at all?
convert([], []).
convert([X,Y|R], [ex(X,Y)|S]) :- convert(R, S).
Tell your prof I said "hi".
I have those predicate:
% Signature: student(ID, Name, Town , Age)/4
% Purpose: student table information
student(547457339, riki, beerSheva , 21).
student(567588858, ron, telAviv , 22).
student(343643636, vered, haifa , 23).
student(555858587, guy, beerSheva , 24).
student(769679696, smadar, telAviv , 25).
% Signature: study(Name, Department , Year)/3
% Purpose: study table information
study(riki, computers , a).
study(ron, mathematics , b).
study(vered, computers , c).
study(riki, physics , a).
study(smadar, mathematics , c).
study(guy, computers , b).
% Signature: place(Department ,Building, Capacity)/3
% Purpose: place table information
place(computers , alon , small).
place(mathematics , markus , big).
place(chemistry , gorovoy , big).
place(riki, zonenfeld , medium).
I need to write predicate noPhysicsNorChemistryStudents(Name , Department , Year , Town)/4:
find all students' name who not learn physics or chemistry.
I don't know how to write it. I think it should be something with cut.
% Signature: noPhysicsNorChemistryStudents(Name , Department , Year , Town)/4
Why this is not true? :
noPhysicsNorChemistryStudents2(Name , Department , Year , Town) :-
student(_, Name, Town, _), study(Name , Department , Year),
pred1(Name , physics , Year ) , pred1(Name , chemistry , Year ).
pred1(N,D ,Y):- study(N , D , Y ) , ! , fail .
Not in Prolog has a weird syntax, on purpose to highlight that could be very different from what people expect. See CWA if you are interested.
The operator is \+, and syntactically it's banal: just prefix a goal with it to get a true when what you know that goal is false, and viceversa.
Then your assignment could read:
noPhysicsNorChemistryStudents(Name , Department , Year , Town) :-
student(_, Name, Town, _),
\+ ( AnyCondition ).
See if you can devise the AnyCondition formula, that surely use study(Name, Department, Year). You could apply Boolean algebra to factorize:
(not A) and (not B) = not (A or B)
edit under CWA, we can use negation as failure. That's the way Prolog implements \+
\+ G :- call(G), !, fail.
add to correct
\+ G.
Should be clear now that if the predicate, with \+ allowed, would be like
noPhysicsNorChemistryStudents(Name, Department, Year, Town) :-
student(_, Name, Town, _),
study(Name, Department, Year),
\+ (study(Name, physics, _) ; study(Name, chemistry, _)).
we can write
noPhysicsNorChemistry(Name) :-
( study(Name, physics, _) ; study(Name, chemistry, _) ), !, fail.
noPhysicsNorChemistry(_).
noPhysicsNorChemistryStudents(Name, Department, Year, Town) :-
student(_, Name, Town, _),
study(Name, Department, Year),
noPhysicsNorChemistry(Name).