Suffix of a list in prolog - prolog

I want to define a predicate suffix (S,L) which holds if S is a list which is a suffix of
list L. For exemple:
?− suffix([a,b,c],[b,c]).
true.
?− suffix([a,b,c],[b,a]).
false.
I tried using suffix(S,L):- append(_,S,L). but this won't work with my examples, shown above.

This works (see https://swish.swi-prolog.org/p/gMgNaCKQ.pl for a working example):
suffix(L,S) :- append(_,S,L).
This also works (see https://swish.swi-prolog.org/p/XqCXArvz.pl for a working example):
suffix( L , L ) . % every list is a suffix of itself, with the empty list [] as its prefix
suffix( [_|L] , S ) :- suffix(L,S) . % otherwise, pop the list and try again.

Related

How do I assign return variable in prolog?

I'm totally new to Prolog so I might be really off here.
I'm trying to solve the following problem for the past week:
tweeted(anne, tweet1).
tweeted(anne, tweet5).
tweeted(fred, tweet2).
tweeted(fred, tweet7).
tweeted(fred, tweet8).
findTweets(Name, TweetsByName) :- findall(X, tweeted(Name, X), TweetsByName).
tweets([], _).
tweets([Name|Names], Result) :-
findTweets(Name, Result),
/* how do I append Result to Result ? */
tweets(Names, Result).
If I call with single variable, there's only one recursion call and I get result:
?- tweets([fred], R).
R = [tweet2, tweet7, tweet8].
But I'm struggling to understand how can I append Result value from findTweets call to Result in order to return accumulative result?
I tried using append function but I had no luck...
for example:
tweets([], _).
tweets([Name|Names], Result) :-
findTweets(Name, Tweets),
append(Tweets, Result, Temp),
Result is Temp,
tweets(Names, Result).
But I get this error:
?- tweets([fred], R).
ERROR: Type error: `character' expected, found `tweet2' (an atom)
ERROR: In:
ERROR: [11] _25712 is [tweet2,tweet7|...]
ERROR: [10] tweets([fred],_25752) at /home/kimchi/git-repos/bbk/Programming-Language-Paradigms/logic-programming-Pavel-Durov/relationships.pl:33
ERROR: [9] toplevel_call(user:user: ...) at /snap/swi-prolog/43/usr/lib/swipl/boot/toplevel.pl:1117
Thanks in advance :)
First, a few notes:
is is only for arithmetic computations on numbers. You cannot use it on lists, you will get an error.
You can never "re-assign" variables in Prolog. You always need to use a new variable if you want to want a name for a new term.
Apart from that, you are on the right track with append. My solution is close to yours, it just needed to be rearranged a bit:
persons_tweets([], []).
persons_tweets([Person | Persons], AllTweets) :-
person_tweets(Person, ThisPersonTweets),
persons_tweets(Persons, OtherPersonsTweets),
append(ThisPersonTweets, OtherPersonsTweets, AllTweets).
I renamed your findTweets to person_tweets to make clearer that it is a relation between one person and a collection of tweets. Similarly, persons_tweets is a relation between a collection (list) of persons and tweets.
Example:
?- persons_tweets([fred, anne], Tweets).
Tweets = [tweet2, tweet7, tweet8, tweet1, tweet5].
Note that the non-recursive case is persons_tweets([], []). You cannot use the anonymous variable _ here. If you have an empty list of persons, you really want to have an empty list of tweets, not just any tweets. For instance, this should fail, but with your version it would succeed:
?- persons_tweets([], [some_tweet]).
false.
I think you want:
?- Names = [fred], findall(Tweet, (member(Name, Names), tweeted(Name, Tweet)), Tweets).
Names = [fred],
Tweets = [tweet2,tweet7,tweet8].
?- Names = [anne, fred], findall(Tweet, (member(Name, Names), tweeted(Name, Tweet)), Tweets).
Names = [anne,fred],
Tweets = [tweet1,tweet5,tweet2,tweet7,tweet8].
This is listing all the tweets that were tweeted by the Names.
There's no reason to use recursion here. You can do it just with findall/3. Try something like this:
tweet( anne , tweet1 ).
tweet( anne , tweet5 ).
tweet( fred , tweet2 ).
tweet( fred , tweet7 ).
tweet( fred , tweet8 ).
tweets( Ps , Ts ) :- findall(T,desired_tweet(Ps,T),Ts).
desired_tweet(Ps,T) :- tweet(P,T), member(P,Ps).
So tweets([fred],Ts) yields the expected Ts = [tweet1, tweet5, tweet2, tweet7, tweet8].
But it fails horribly if the list of authors is a variable. And it's nice if prolog predicates behave symmetrically. So lets add a couple of helpers here:
This lets the user specify a single name as an atom (a likely common use case):
tweets( P , Ts ) :- atom(P) , ! , tweets([P],Ts) .
And this lets the user leave the persons list as an unbound variable, which will results in retrieving all the tweets (and generating a list of authors):
tweets( Ps , Ts ) :- var(Ps) , ! , setof(P,T^tweet(P,T),Ps) , tweets(Ps,Ts) .
[Note: the expression in the setof/3 invocation, T^tweet(P,T) is an existential quantifier. It tells setof/3 to ignore the values of T in determining the set, so you get the distinct set of P rather than the distinct set of [P,T] pairs.
If you put it all together,
tweet( anne , tweet1 ).
tweet( anne , tweet5 ).
tweet( fred , tweet2 ).
tweet( fred , tweet7 ).
tweet( fred , tweet8 ).
tweets( P , Ts ) :- atom(P) , ! , tweets([P],Ts) .
tweets( Ps , Ts ) :- var(Ps) , ! , setof(P,T^tweet(P,T),Ps) , tweets(Ps,Ts) .
tweets( Ps , Ts ) :- findall(T,desired_tweet(Ps,T),Ts).
desired_tweet(Ps,T) :- tweet(P,T), member(P,Ps).
You can says tweets(anne,Ts) and get the expected Ts = [tweet1, tweet5].
And, similarly, you can say tweets(Ps,Ts) and get
Ps = [anne, fred],
Ts = [tweet1, tweet5, tweet2, tweet7, tweet8]

prolog replacing elements in list

I need help with understanding how to work with lists like that [a(b,c),a(x,d)]
change(S,K,R) changes first list [a,c,b] with values given in second list [c(a,x),c(b,y)]
?- change([a,c,b],[k(a,x),k(b,y)],R).
R = [x,c,y].
% my program but it works with second list that is of wrong list elements type k(a,x) but like [a,x] and works kinda poorly cause return True instead of R= x,c,y, if i print R value it is [y,c,x|_2826]
i call my code with ?- change([a,c,b],[a,x,b,y],R).
change([],[],[]):-!.
change([],[],R):-write(R),!.
change([H1|T1],[],[H1|R]):-change(T1,[],R),!.
change([H1|T1],[H2,H3|T2],R) :-
( ( H1==H2 , change(T1,T2,[H3|R]) )
; ( H1\==H2, change(T1,[H2,H3|T2],[H1|R]) )
).
Looks like you should use Association lists.
see SWI-Prolog manual
and the online doc

use operator as constructor in prolog

I'm writing a function called leagalCourse, it takes just one parameter, a course list. A course like, for example, john+mary+94 would represent a project done by John and Mary with a mark of 94.
It should be true if the course data is “legal”, which means that it must not have a project with the same name twice such as john+john+70.
There also must not be two projects in the list containing the same pair of students. So if there’s a project harry+ron+82 in the list, it would be illegal for the list also to contain harry+ron+90 or ron+harry+63.
There is a sample output:
?- legalCourse([one+two+3,four+five+6,one+six+7]).
true.
?- legalCourse([one+two+3,four+four+6,one+six+7]).
false.
?- legalCourse([one+two+3,four+five+6,one+two+7]).
false.
?- legalCourse([one+two+3,two+one+6,one+six+7]).
false.
This is what I tried:
legalCourse([]).
legalCourse(X) :-
diffName(X).
legalCourse([Project|M]):-
diffName(Project),
not(samePair([Project|M])),
legalCourse(M).
diffName(Name1+Name2+_) :-
Name1 \= Name2.
/*can not have duplicated group*/
samePair([Name1+Name2+_|More]) :-
append([[head],tail,More]),
member(Name1,[head]),
member(Name2,[head]).
The function partially worked before I added the samePair predicate.
I think this works, you need to switch the vars and check both are different in check_no_dups/1.
legalCourse(List):-
maplist(triple_double,List,ListDouble),
check_no_dups(ListDouble).
check_no_dups([]).
check_no_dups([H|T]):-
H =X+Y,
maplist(dif(H),T),
H2 =Y+X,
maplist(dif(H2),T),
check_no_dups(T).
triple_double(X+Y+_Z,X+Y):-dif(X,Y).

What am I doing wrong in declaring a predicate for use with a DCG in Prolog?

I'm using the following to check if a string is valid for use with my grammar:
id(ID) :-
atom_chars(ID, [H|T]),
is_alpha(H),
ensure_valid_char(T).
ensure_valid_char([H|T]) :-
H == '_';
is_alpha(H);
atom_number(H, _),
ensure_valid_char(T).
It basically just checks that it starts with an alphabetic character, and after that it can be alphanumeric or an underscore.
I cannot seem to figure out how to get this to work with my DCG/grammar though.
This is its current structure where the predicate would be used:
typeID(Result) --> ['int'], id(ID), {
Result = ['int', ID]
}.
Where basically I'm saying a typeID is an integer type declaration followed by an identifier (int foo would be an example), and then I format it into a list and "give it back".
But in this case it's saying "id" is an undefined predicate. How do I use it so that I'm still able to access what ID holds to be able to format it, and still ensure that it's an ID using the predicate?
If I try:
id(ID) --> {
atom_chars(ID, [H|T]),
is_alpha(H),
ensure_valid_char(T),
ID = ID
}.
I get the error that:
atom_chars/2: Arguments are not sufficiently instantiated
Please use more readable names. The Prolog convention is to use underscores for readability.
This is_because_using_underscores_makes_even_long_names_readable, butUsingMixedCapsDoesNotAndMakesYourCodeAsUnreadableAsJava.
Second, please avoid unnecessary goals. A goal like ID=ID always holds, so you can as well remove it.
Third, a common pattern when describing the longest match in DCGs is to use clauses like the following:
symbol([A|As]) -->
[A],
{ memberchk(A, "+/-*><=") ; code_type(A, alpha) },
symbolr(As).
symbolr([A|As]) -->
[A],
{ memberchk(A, "+/-*><=") ; code_type(A, alnum) },
symbolr(As).
symbolr([]) --> [].
You can use this in a DCG like this:
id(Atom) --> symbol(Codes), { atom_codes(Atom, Cs) }
The longest match of symbol//1 will be the first solution.
All of this requires that you have the Prolog flag double_quotes set to codes.
semicolon has higher precedence than comma
?- (N=(',') ; N=(';')), current_op(P,T,N).
N = (','),
P = 1000,
T = xfy ;
N = (;),
P = 1100,
T = xfy.
then ensure_valid_char/1 doesn't have the structure you expect. It should be
ensure_valid_char([H|T]) :-
( H == '_' ;
is_alpha(H) ;
atom_number(H, _) % ugly
),
ensure_valid_char(T).
and a simpler issue: you're missing the base case of the recursion
ensure_valid_char([]).

Prolog, how to show multiple output in write()

go :- match(Mn,Fn),
write('--Matching Result--'),
nl,
write(Mn),
write(' match with '),
write(Fn),
match(Mn1,Fn1).
person(may,female,25,blue).
person(rose,female,20,blue).
person(hock,male,30,blue).
person(ali,male,24,blue).
match(Mn,Fn):-person(Fn,'female',Fage,Fatt),
person(Mn,'male',Mage,Matt),
Mage>=Fage,
Fatt=Matt.
Hi,this is my code...but it's only can show the 1 output...but there are 3 pair of matching in match(X,Y).how to show them all in my go function.
Thank you
You get all your matches if you force backtracking, usually by entering ; (e.g. in SWI Prolog). But you also see that you are getting unnecessary outputs true. This is because the last clause in go is match(Mn1,Fn1). This clause succeeds three times and binds the variables Mn1,Fn1 but then only true is output, because you do not write() after that clause. The fourth time match(Mn1,Fn1) fails and by backtracking you come back to the first clause match(Mn,Fn) that matches, the match is output, etc.
You surely do not want to have this behavior. You should remove the last clause match(Mn1,Fn1) in go. Now by pressing ; you get the 3 matches without any output true in between.
But what you likely want is that the program does the backtracking. To achieve this, you just need to force backtracking by adding false as the last clause. To get proper formatting of the output, use the following program. The last clause go2. is added to get true at the very end.
go2 :- write('--Matching Result--'), nl,
match(Mn,Fn),
write(Mn), write(' match with '), write(Fn), nl,
fail.
go2.
This technique is called failure driven loop.
If you have any predicate that has multiple results and want to to find all of them, you should use findall/3
For example, in your case, you could do something like:
findall([X,Y], match(X,Y),L).
L will be a list that will contain all the X,Y that satisfy match(X,Y) in the format [X,Y].
for example, assuming that:
match(m1,f1).
match(m2,f2).
the result will be L = [ [m1,f1], [m2,f2] ]
note that you can define the format as you wish, for example you could write:
findall(pair(X,Y), match(X,Y), L).
L = [ pair(m1,f1), pair(m2,f2) ]
findall( X, match(X,Y), L).
L = [ m1, m2]
findall( 42, match(X,Y), L).
L = [42, 42]
then you have to recurse on the list to print them.
However, if you wish to find one result, run some code and then continue you could use forall/2:
forall(match(X,Y), my_print(X,Y).
Prolog is a lazy language. Which means that it will stop once it has found a condition that made your problem true. This will be the very first match alone.
IF your code is working (I haven't tried it), then you should try and run the match-statement like this in your prolog inspector: match(X,Y)
The prolog inspector will return all states and print them for you.

Resources