4

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.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
titusfx
  • 1,896
  • 27
  • 36
  • 1
    Are you comfortable with the concept of relational databases? –  Sep 30 '15 at 06:22
  • 2
    What would you expect as a result for a non-ground fact like ```succ(X,s(X))``` ? The query ```:- succ(agora, Y).``` succeeds, but is this also what you want? What about predicates from the library? ```:- member(agora, List).``` succeeds as well. – lambda.xy.x Sep 30 '15 at 08:14
  • 3
    First off, "how can I list all the properties of an atom" is a nonsensical question in the context of Prolog. Then, when you define tables of facts, this is your "database schema", if you will. If you know your database schema, you can write queries against it. There are cases when you might want to dynamically query the schema itself, but I highly doubt that this is your intention. TL;DR You are trying to solve a problem of your own making. –  Sep 30 '15 at 12:17
  • 2
    As other have already hinted, I also think that you need a different approach. It would help if you provided more details on the problem you're trying to solve, however. It seems like you have objects (movies?) and properties of those objects (director, duration). If so, it would name sense to use a module or object approach to represent your data. This would allow you to easily query an object about its properties. – Paulo Moura Sep 30 '15 at 15:07
  • @Boris I'm more or less confortable with those concepts. And yes, I want dynamically query the schema itself. – titusfx Oct 01 '15 at 08:00
  • 1
    If you indeed want to query the schema (still not convinced: probably a better database design with proper normalization will make this unnecessary), take a good look at the [answer by @CapelliC](http://stackoverflow.com/a/32866058/1812457). Importantly, putting everything in a module will make his approach cleaner. –  Oct 01 '15 at 08:15
  • Still not clear. This is how it goes, normally: you have a database, it contains information. Then you write queries, and Prolog tries to prove them based on the database. If a query can be proved, it succeeds, reporting the variable bindings that were made. This is your "true" or "yes" + the variable bindings. If, based on the available data, the query cannot be proved to be true, it fails. This is your "false" or "no". Can you describe (even without any code) what your data is, what your questions will be, and what answers you would expect? –  Oct 01 '15 at 11:03
  • PS: as it is right now, I cannot figure out who invites whom to what etc. If a human does not quite understand, how would you ever explain it to a computer??? –  Oct 01 '15 at 11:05
  • @Boris, I want to modele a dialog, between two people. One person with name Sa, say to another with name Ma, if he want's to watch a Agora (a movie). And the other says: I don't like that Director. Which means, for me, that Ma said No (which is false in my Answer variable) to the invitation. This is a simple example, if I want to generalize this type of reasoning, which is, if someone says don't like something about the movie, that's mean , I don't watch the movie. – titusfx Oct 01 '15 at 11:10
  • @Boris, In my database I have that Ma doesn't like the director of the movie. That Sa make an invitation to Ma, and the Rule (which is I'm trying to generalize) is if someone (Ma) says I dont like something about a movie, that mean, I don't want to watch that movie. – titusfx Oct 01 '15 at 11:12
  • Please write all this stuff in your question, carefully, as if you are explaining it to an idiot. Try to keep it as small as possible (minimal useful example is best). –  Oct 01 '15 at 11:36
  • 2
    I think it would be good if you could change how you store your data so it is like rdf triples. i.e. relation(agora, is,movie). relation('Alejandro Amenabar', directs, agora). relation(agora, duration, 2h). – user27815 Oct 01 '15 at 11:41
  • @user27815 indeed, like augu answer, but the problem is to write for each director, genre , property of the object the same thing, it's not clean declarative code. I thoug using meta-programming I could doit in a clean way. – titusfx Oct 01 '15 at 11:52

6 Answers6

3

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 ? 
Isabelle Newbie
  • 9,258
  • 1
  • 20
  • 32
titusfx
  • 1,896
  • 27
  • 36
  • @CapelliC, I couldn't use clause/2 I get an permission error. So my solution is without it. For you exmplanation it will be slower but at least easier to read. ;) Thanks – titusfx Oct 02 '15 at 10:34
2

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.

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • I didn't understand your answer until now, that I try to make my own. I did more or less the same as you, but I didn't use clause, I don't know why did you use it. I came up with a general solution, and an specific one like you but shorter. I will post the general one. ?- current_predicate(PredicateName/Arity), Arity > 1, Term =.. [PredicateName, agora, OtherParameter], Term. – titusfx Oct 02 '15 at 08:44
  • 2
    clause/2 allows to inspect the database (predicates), **without** calling them, so, without introducing (potentially difficult to find) problems due to collateral effects implied by evaluation. Also, be sure you understand the comment by @lambda.xy.x – CapelliC Oct 02 '15 at 08:52
0

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).
Community
  • 1
  • 1
user27815
  • 4,767
  • 14
  • 28
  • 2
    This might be correct but it is a very bad idea from the start. OP seems to have trouble figuring out what his data should look like, and how to query it. –  Sep 30 '15 at 12:14
  • I was imagining it would be useful if you had a large file and you did not know what predicates were defined. How would you return a list of predicates.. I thought something like listing/0 would work but it just prints to console and does not return a list. – user27815 Sep 30 '15 at 12:17
  • 2
    If the file is correct Prolog, it is still better to consult it. You can easily enumerate all predicates using [`current_predicate/1`](http://www.swi-prolog.org/pldoc/doc_for?object=current_predicate/1), esp. if this was a module file (as [here](http://stackoverflow.com/questions/31456084/listing-predicates-within-a-given-library-module/31456816#31456816)) –  Sep 30 '15 at 12:20
  • current_predicate/1 returns lots of things that are not in the file though? Like 'prolog_event_hook/1'. So you would have to filter them? If the file is not a module, you would not be able to make it module with out knowing all the predicates in it. So that you can put them at the top of the file. – user27815 Sep 30 '15 at 12:26
  • 3
    You can wrap it in a module without changing the original file. This is all completely beside the point though: what OP is trying to do is almost certainly unnecessary. –  Sep 30 '15 at 12:28
  • 1
    @user27815 this is a solution, I thought that too, didn't look declarative. I want to be declarative. – titusfx Oct 01 '15 at 08:06
0

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).
augu
  • 3
  • 4
  • This is exactly what I want but I don't want to write property(NameProperty, Object, Value) for every object that I want to use or for every object to get all it's properties. – titusfx Oct 01 '15 at 08:59
  • 1
    @Mauricio You still have not explained your use case in your question. I have a sneaking suspicion that if you made the effort to properly define your problem you will find a straight-forward solution to it. –  Oct 01 '15 at 10:38
  • @Boris, thanks. I just edit my question. I'm still stuck into, I'm new in prolog :/ – titusfx Oct 01 '15 at 10:58
0

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).
user27815
  • 4,767
  • 14
  • 28
0

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.

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33