Select a node in the DOM with xpath/3 - xpath

I have a problem to extract a specific element in DOM.
For example i use this code:
xpath(DOM, //table(1), TABLE)
and i have access to the first table. But if i try to access to the second table i use this code:
xpath(DOM, //table(2), TABLE)
but it fail and i haven't the second table.
Is it possible to select a specific node in the DOM with xpath/3?

I guess you're reusing the same variable, and that can't work:
:- [library(xpath)].
table(N, T) :-
DOM = element(xx, [],
[ element(table, [], [a,b,c]),
element(table, [], [x,y,z])
]),
xpath(DOM, //table(N), T).
with that fragment, as expected
?- table(2,T).
T = element(table, [], [x, y, z])
but
?- table(1,T), table(2,T).
false.

Selection (or predicates) in xpath are embedded in []-brackets.
For example
//table[1]
//table[2]
//table[last()-1]
//table[#someattribute='somevalue']
etc.
EDIT: It seems I missed the prolog-tag. In prolog it seems that xpath predicates are not specified by square brackets but by parenthesis: http://www.swi-prolog.org/pldoc/man?predicate=xpath%2F3 . But according to that documentation your code sample should work as intended.

Related

Checking if first three elements of list are same data type.Prolog

I am begginer in Prolog.I have my custom types bird,fish,animal.So the problem is i want to pass function like firstThree([owl,eagle,chicken,cat,dog]). and
get result true because first three are same data type.For another example:
firstThree([dog,cat,mouse,owl,shark,eagle]). > true
firstThree([shark,dog,owl,mouse]). > false
This is my base data:
bird(eagle).
bird(chicken).
animal(cat).
animal(mouse).
animal(dog).
fish(shark).
fish(wheal).
fish(goldfish).
isSameType(X,Y):- bird(X),bird(Y);animal(X),animal(Y);fish(X),fish(Y).
I have tried this but it doesnt really work:
firstThree([H1,H2,H3|T]):-
isSameType(H1,H2),
areSameType([H2,H3|T]).
Assuming isSameType/2 is working,
firstThree([H1,H2,H3|_]) :-
isSameType(H1, H2),
isSameType(H2, H3).
ought to be sufficient, because equality is transitive (if H1 = H2 and H2 = H3 then H1 = H3).
In the future it would probably behoove you to try something like:
critter_type(Critter, bird) :- bird(Critter).
critter_type(Critter, animal) :- animal(Critter).
critter_type(Critter, fish) :- fish(Critter).
isSameType(X, Y) :- critter_type(X, Type), critter_type(Y, Type).

Ask Prolog for predicates of an argument [duplicate]

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).

Get values of a cell using lists in prolog

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.

Prolog add to list if element is not equal to string

I am trying to create a list of pairs which have a non-null Guard element.
get_only_guarded(L) :-
Guard \= null,
findall([S,D],transition(S,D,_,Guard,_),L).
This is what ive tried but it doesnt seem to work. What would be the correct way?
Use dif(Guard,null) instead. Handle (\=)/2 with care!
a more 'conventional ' way to do: push the test after the instantiation
get_only_guarded(L) :-
findall([S,D], (transition(S,D,_,Guard,_),Guard \= null), L).

Handle table xpath/3

I have noticed that if i use xpath(DOM, //table(N), T), the system extracts only the external tables, but if i have a table into another table, i can see it only i use findall/3. The predicate findall/3 extract all table, but i can't access to internal table. Why?
I use
xpath(DOM, //table(N), T)
to extract the N-th table, but the system extracts only external table. If i have for example:
<table>
<table>
</table>
<table>
i can't use xpath(DOM, //table(N), T), because i would have only one table. If I insert //table(2), the system fails. Instead, if i use findall/3 i have all table in the DOM, but with findall i haven't access to the n-th table. How can i do?
indexing works 'on levels', to access nested elements you can specify the path, or - if the path is only partially known - use multiple xpath calls.
test data (way too simple)
?- D=element(table,[id=one],[element(table,[id=two],[])]).
raw search operator indexed:
?- xpath($D, //table(N), T).
N = last,
T = element(table, [id=one], [element(table, [id=two], [])]) ;
N = last,
T = element(table, [id=two], []) ;
false.
fully known path
?- xpath($D, //table/table(#id), ID).
ID = two ;
false.
multiple xpath 'joined' (this is most flexible, but the behaviour of search operators need some apprenticeship...)
?- xpath($D, //table, T), xpath(T, table(#id), ID).
T = element(table, [id=one], [element(table, [id=two], [])]),
ID = two ;
false.

Resources