I'm trying to implement a predicate in prolog where given any sample table with data, the contents of the table indices [+I, +J] should be printed on the terminal. In this case,
Here is the prototype of the predicate:
get_table_values_cell([+I,+J],+Table,-ContentsL)
Sample output:
?- table(sample_table4,Data),get_table_values_cell([3,2],Data,CL).
Data = [[["11"], ["21"], ["31"], ["41"]], [["12"], ["22"], ["32"]],
[["13"], ["23"]], [["14"]]],
CL = ["32"].
Thanks in advance!
The problem could be easily solved with nth0/3 or nth1/3 (built in predcates), for instance in this way:
get_table_values_cell([R,C],L):-
Data = [
[["11"], ["21"], ["31"], ["41"]],
[["12"], ["22"], ["32"]],
[["13"], ["23"]],
[["14"]]
],
nth1(R,Data,L1),
nth1(C,L1,L).
?- get_table_values_cell([3,2],CL).
CL = ["23"].
To solve it without using nth0/3 or nth1/3, you can write your own "find" (here called getElement1/4) predicate in this way:
getElement1(C,C,[H|_],H):- !. %the cut is not mandatory
getElement1(Current,Max,[_|T],Lout):-
Current < Max,
C is Current+1,
getElement1(C,Max,T,Lout).
and then
getElement([R,C],L):-
Data = [
[["11"], ["21"], ["31"], ["41"]],
[["12"], ["22"], ["32"]],
[["13"], ["23"]],
[["14"]]
],
getElement1(1,R,Data,L1),
getElement1(1,C,L1,L).
?- getElement([3,2],L).
L = ["23"]
BTW i suggest you to use built in predicates.
Related
another way to ask the question is:
How I can list all the properties of an atom?
For example:
movie(agora).
director(agora, 'Alejandro Amenabar')
duration(agora, '2h').
so, I will like to receive all the predicates that has agora for argument. In this case it will be: movie, director, duration, with the other parameters ('Alejandro Amenabar', '2h').
I found: this, and this questions, but I couldn't understand well.
I want to have the value of false in the "variable Answer" if PersonInvited doesn't like something about the movie.
My query will be:
answer(Answer, PersonInvited, PersonWhoMadeInvitation, Movie)
Answer: I don't like this director
answer(false, PersonInvited, PersonWhoMadeInvitation, Movie):-
director(Movie, DirectorName),not(like(PersonInvited,DirectorName)).
The same thing will happen with any property like genre, for example.
Answer: I don't like this genre
answer(false, PersonInvited, PersonWhoMadeInvitation, Movie):-
genre(Movie, Genre), not(like(PersonInvited,Genre)).
So, I want to generalize this situation, instead of writing repeatedly every feature of every object.
I found two solutions the 2nd is cleaner from my point of view, but they are different.
Parameters:
PredName: Name of the predicate.
Arity: The Arity of the Predicate.
ParamValue: If I want to filter by one specific parameter.
PosParam: Which is the position of the parameter in the predicate.
ListParam: All the value of the posibles values parameters (mustbe a Variable all the time).
Solution 1:
filter_predicate(PredName, Arity, ParamValue,PosParam, ListParam):-
current_predicate(PredName/Arity),
Arity >= PosParam,
nth(PosParam, ListParam, ParamValue),
append([PredName], ListParam, PredList),
GlobalArity is Arity + 1,
length(PredList, GlobalArity),
Predicate =.. PredList,
Predicate.
Query
filter_predicate(PredName, Arity, agora, 1, Pm).
Output
Arity = 2
Pm = [agora,'Alejandro Amenabar']
PredName = director ?
yes
Solution2:
filter_predicate(PredName, Arity, ParamList):-
current_predicate(PredName/Arity),
append([PredName], ParamList, PredList),
GlobalArity is Arity + 1,
length(PredList, GlobalArity),
Predicate =.. PredList,
Predicate.
Query 1:
filter_predicate(PredName, Arity, [agora, X]).
Output
Arity = 2
PredName = director
X = 'Alejandro Amenabar' ?
Query 2:
filter_predicate(PredName, Arity, [X, 'Alejandro Amenabar']).
Output
Arity = 2
PredName = director
X = agora ?
here is my attempt, using SWI-Prolog
?- current_predicate(so:F/N), N>0, length(As,N), Head =.. [F|As], clause(so:Head,Body), As=[A|_], A==agora.
note that I coded into a module called so the facts, so I qualify with the module name the relevant calls. Such builtins (clause/2 and current_predicate/1) are ISO compliant, while modules (in SWI-prolog) are not. So I'm not sure about portability, etc...
clause/2 it's a builtin that allows for easy writing metainterprets. See the link for an awesome introduction to this Prolog historical 'point of strength'.
The 2 last calls (I mean, As=[A|_], A==agora) avoid matching clauses having a variable as first argument.
Using reading lines into lists with prolog
All your predicates are in a file 'my_file.pl'.
e.g. my_file.pl contains:
movie(agora).
director(agora, 'Alejandro Amenabar').
duration(agora, '2h').
You can use:
getLines(File,L):-
setup_call_cleanup(
open(File, read, In),
readData(In, L),
close(In)
).
readData(In, L):-
read_term(In, H, []),
( H == end_of_file
-> L = []
; L = [H|T],
readData(In,T)
).
pred_arg_file(Pred,Argue,File):-
getLines(File,L),
member(M,L),
M=..List,
member(Argue,List),
List=[Pred|_].
Then you can query:
?-pred_arg_file(Pred,agora,'my_file.pl').
Pred = movie ;
Pred = director ;
Pred = duration ;
false
or
?- findall(Pred,pred_arg_file(Pred,agora,'my_file.pl'),Preds).
Preds = [movie,director,duration].
If you want to return the properties, return the whole List not just the head.
pred_arg_file(List,Argue,File):-
getLines(File,L),
member(M,L),
M=..List,
member(Argue,List).
From my understanding you should change your data representation so that you can query the relations.As other answers have pointed out, So use triples, you can easily write code to change all your relations into this form as a one off. You then need to work out what the best way to store likes or dislikes are. This will effect how negation works. In this example:
relation(starwars,is,movie).
relation(lucas, directs,starwars).
relation(agora, is,movie).
relation('Alejandro Amenabar', directs, agora).
relation(agora, duration, '2h').
like(ma,'Alejandro Amenabar').
like(ma,movie).
like(ma,'2h').
ma_does_not_want_to_go(Film):-
relation(Film,is,movie),
relation(Film,_,Test), \+like(ma,Test).
ma_does_not_want_to_go(Film):-
relation(Film,is,movie),
relation(Test,_,Film), \+like(ma,Test).
ma_wants_to_go(Film):-
relation(Film,is,movie),
\+ma_does_not_want_to_go(Film).
sa_invites_ma(Film,true):-
ma_wants_to_go(Film).
sa_invites_ma(Film,false):-
ma_does_not_want_to_go(Film).
A draft of a solution using Logtalk with GNU Prolog as the backend compiler:
% a movie protocol
:- protocol(movie).
:- public([
director/1,
duration/1,
genre/1
]).
:- end_protocol.
% a real movie
:- object('Agora',
implements(movie)).
director('Alejandro Amenabar').
duration(120).
genre(drama).
:- end_object.
% another real movie
:- object('The Terminator',
implements(movie)).
director('James Cameron').
duration(112).
genre(syfy).
:- end_object.
% a prototype person
:- object(person).
:- public([
likes_director/1,
likes_genre/1
]).
:- public(likes/1).
likes(Movie) :-
conforms_to_protocol(Movie, movie),
( Movie::genre(Genre),
::likes_genre(Genre) ->
true
; Movie::director(Director),
::likes_director(Director) ->
true
; fail
).
:- end_object.
% a real person
:- object(mauricio,
extends(person)).
likes_director('Ridlye Scott').
likes_genre(drama).
likes_genre(syfy).
:- end_object.
Some sample queries:
$ gplgt
...
| ?- {movies}.
...
(5 ms) yes
| ?- mauricio::likes('Agora').
true ?
yes
| ?- mauricio::likes(Movie).
Movie = 'Agora' ? ;
Movie = 'The Terminator' ? ;
no
| ?- 'The Terminator'::director(Director).
Director = 'James Cameron'
yes
The code can be improved in several ways but it should be enough to give you a clear idea to evaluate this solution.
If I understood your question properly I propose the follow:
What if you change your schema or following this idea you can make a method that simulate the same thing.
class(movie, agora).
property(director, agora, 'Alejandro Amenabar').
property(duration, agora, '2h').
If do you want the types of agora, the query will be:
class(Type, agora)
If you want all the properties of agora, that will be:
property( PropertyName, agora, Value).
How to define member (X,L) using concatenating relationship? could anyone explain the method?
concatenate([],L,L).
concatenate([X|L],L2,[X|CL]):-concatenate(L,L2,CL).
It is given concatenating goals
Try the behaviour of concatenate/3 in your Prolog interpreter (the command line, AKA REPL) for this specific query:
?- L=[1,2,3], concatenate(_,[X|_],L).
This pattern it's actually what you need... but the details of adding a predicate member/2 will depend on the actual Prolog implementation.
I'd call it member/2 predicate, not a member function,
it sounds like the C++ term.
Here's concat_member/2:
concat_member( X, Xs ) :- concat( _ , [ X | _ ], Xs ).
Trying to create a predicate (timePeriod/2) that calculates the time period between two dates for a specific fact. I've managed to do this by myself, but face issues when 'other answers' exist in the same list (i.e. easier to explain with examples).
I have the following knowledge-base facts;
popStar('Jackson',1987,1991).
popStar('Jackson',1992,1996).
popStar('Michaels',1996,2000).
popStar('Newcastle',2000,2007).
popStar('Bowie',2008,2010).
And the following function, calculates the time between dates for a specific fact (as per below).
Predicate (timePeriod/2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Using Bowie as an example; it returns X=3 (which is correct).
However, when there is repetition in the list, with more than one answer available, the predicate just states 'false'. Using the facts 'Jackson' as an example, I want to be able to calculate both of the time periods for both facts; at the same time.
So, if the predicate would work for both of the Jackson facts, the predicate timePeriod would state X=10.
Would really appreciate if anyone could suggest what to change in order for this to work correctly.
Thanks.
You probably don't quite understand what foreach/3 does. I don't think I fully understand foreach/3 either. I know for sure that it is not the same as say:
for each x in xs:
do foo(x)
Another thing: "tuples" in Prolog are not what you might expect, coming from a language like Python or Haskell. This: (a,b,c) is actually this: ','(a,','(b,c)). Much better is to use a flat term, the generic form would be triple(a,b,c). For a pair, the idiom is First-Second.
So, you can simplify your call to bagof/3 to this:
?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
Once you have a list as above, you need to sum the differences, which would be maybe something like:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
And then you can query like this:
?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.
?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.
SWI-Prolog has a nice library to handle aggregation: it builds upon standard 'all solutions' predicates like findall/3,setof/3,bagof/3, so you should first grasp the basic of these (as Boris explained in his answer). With the library, a single query solves your problem:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).
Trying to create a predicate (timePeriod/2) that calculates the time period between two dates for a specific fact. I've managed to do this by myself, but face issues when 'other answers' exist in the same list (i.e. easier to explain with examples).
I have the following knowledge-base facts;
popStar('Jackson',1987,1991).
popStar('Jackson',1992,1996).
popStar('Michaels',1996,2000).
popStar('Newcastle',2000,2007).
popStar('Bowie',2008,2010).
And the following function, calculates the time between dates for a specific fact (as per below).
Predicate (timePeriod/2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Using Bowie as an example; it returns X=3 (which is correct).
However, when there is repetition in the list, with more than one answer available, the predicate just states 'false'. Using the facts 'Jackson' as an example, I want to be able to calculate both of the time periods for both facts; at the same time.
So, if the predicate would work for both of the Jackson facts, the predicate timePeriod would state X=10.
Would really appreciate if anyone could suggest what to change in order for this to work correctly.
Thanks.
You probably don't quite understand what foreach/3 does. I don't think I fully understand foreach/3 either. I know for sure that it is not the same as say:
for each x in xs:
do foo(x)
Another thing: "tuples" in Prolog are not what you might expect, coming from a language like Python or Haskell. This: (a,b,c) is actually this: ','(a,','(b,c)). Much better is to use a flat term, the generic form would be triple(a,b,c). For a pair, the idiom is First-Second.
So, you can simplify your call to bagof/3 to this:
?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = 'Bowie',
Ts = [2008-2010] ;
Name = 'Jackson',
Ts = [1987-1991, 1992-1996] ;
Name = 'Michaels',
Ts = [1996-2000] ;
Name = 'Newcastle',
Ts = [2000-2007].
Once you have a list as above, you need to sum the differences, which would be maybe something like:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
And then you can query like this:
?- bagof(From-To, pop_star('Jackson', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.
?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = 'Bowie',
Ps = [2008-2010],
T = 3 ;
Name = 'Jackson',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = 'Michaels',
Ps = [1996-2000],
T = 5 ;
Name = 'Newcastle',
Ps = [2000-2007],
T = 8.
SWI-Prolog has a nice library to handle aggregation: it builds upon standard 'all solutions' predicates like findall/3,setof/3,bagof/3, so you should first grasp the basic of these (as Boris explained in his answer). With the library, a single query solves your problem:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).
Given this program, why am I forced to define every atom in the predicate, even if they're anonymous. Why is it that undefined variables in a dict predicate aren't thought of as anonymous?
funt2(X) :-
X = point{x:5, y:6}.
evalfunt(point{x:5, y : 6}) :-
write('hello world!').
evalfunt(point{x:_, y : _} ) :-
write('GoodBye world!').
Why can't I just say
evalfunt(point{x:5}) :-
write('GoodBye world!').
^that won't match, by the way.
I may as well just use a structure if I have to define every possible value in the dict to use dicts.
What's the motivation here? Can I do something to make my predicate terse? I'm trying to define a dict with 30 variables and this is a huge roadblock. It's going to increase my program size by a magnitude if I'm forced to define each variables (anonymous or not).
Dict is just a complex data type, like tuple, which has data AND structure. If you have, for example two facts:
fact(point{x:5, y:6}).
fact(point{x:5}).
Then the query
fact(point{x:_}).
will match the second one, but not the first one.
And the query
fact(point{x:_, y:_}).
Will match the first one, but not the second.
Now, if you want to match facts of the form fact(point{x:_, y:_, z:_}) only by one specific field, you can always write a helper rule:
matchByX(X, P) :- fact(P), P=point{x:X, y:_, z:_}.
So having facts:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2, z:3}).
fact(point{x:2, y:65, z:4}).
and quering
matchByX(1, P).
will return:
P = point{x:1, y:2, z:3}
UPDATE:
Moreover, in SWI-Prolog 7 version the field names can be matched as well, so it can be written in much more generic way, even for facts with different structures:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2}).
fact(point{x:2}).
fact(point{x:2, y:2}).
matchByField(F, X, P) :- fact(P), P.F = X.
So query:
?- matchByField(x, 2, P).
P = point{x:2} ;
P = point{x:2, y:2}.
I was able to accomplish what I needed by doing the following
checkiffive(Y) :-
get_dict(x, Y, V), V=5.
You need to use the built in methods for unifying values from a dict.
Described in chapter 5.4 of the SWI prolog reference
http://www.swi-prolog.org/download/devel/doc/SWI-Prolog-7.1.16.pdf