matching 'connectors' and creating structure?

Viewed 71

I'm trying to match and then convert to structure :

text1, connector, text2 ==> connector(text1,text2)

the following works, but seems too unwieldy and non-idiomatic :

p([]) --> [].
p([H|T]) --> [H], p(T).
conn(C) :- member(C,[on,in]).
req(C) --> p(X), [Fun], p(Y), { C =.. [Fun,X,Y], conn(Fun) }.

what is a better way of doing this ?


there was some questions, so here is the big picture... after some probing here is where it stands..

I want to parse simplified English, so that it can generate facts,rules,actions,questions-search,etc... which will be used in a Chat like env i.e. Q/A, req/reply, train-learn...loops

This is not very suitable for linguistic-parsing (np,vp,noun,..), but is better to first parse it for language-templates like a programming language..

Templates like : ifthen, repeat, while, in, into, on, part-of, isa, ...etc.

... (later i was thinking to preparse the POS information with python-spacy if i need it /which solves POS problem/, because prolog does not have NN-tagging. There is python-prolog bridge and python also have better CLI lib)

2 Answers

It's good that you reduced your program to a minimal example, unfortunately it has become a bit too minimal. Based on your other questions, I'll start with the following version of your code:

noun(box) --> [box].
noun(table) --> [table].

conn(C) :- member(C,[on,in]).

req(C) --> noun(X), [Fun], noun(Y), { C =.. [Fun,X,Y], conn(Fun) }.

The following works:

?- phrase(req(Req), [box, on, table]).
Req = on(box, table) ;
false.

But the most general query does not work:

?- phrase(req(Req), Phrase).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [12] _4008=..[_4014,box|...]

This is unfortunate, since the most general query is very handy: It gives you some "testing for free", allowing you to run your code without having to think of test cases. The reason for the error here is that we are trying to apply =../2 with a variable function symbol.

I would write this grammar more like this:

noun(box) --> [box].
noun(table) --> [table].

connector(X, Y, on(X, Y)) -->
    [on].
connector(X, Y, in(X, Y)) -->
    [in].

req(C) -->
    noun(X),
    connector(X, Y, C),
    noun(Y).

This is a bit more explicit, but it allows us to test (exhaustively, because there is no recursion!) with the most general query:

?- phrase(req(Req), Phrase).
Req = on(box, box),
Phrase = [box, on, box] ;
Req = on(box, table),
Phrase = [box, on, table] ;
Req = in(box, box),
Phrase = [box, in, box] ;
Req = in(box, table),
Phrase = [box, in, table] ;
Req = on(table, box),
Phrase = [table, on, box] ;
Req = on(table, table),
Phrase = [table, on, table] ;
Req = in(table, box),
Phrase = [table, in, box] ;
Req = in(table, table),
Phrase = [table, in, table].

Concrete tests work as well (this is hopefully not surprising):

?- phrase(req(Req), [box, on, table]).
Req = on(box, table) ;
false.

Letting the connector rules construct the term allows them to do extra work for specific connectors. For example, we might want to treat "inside" as a synonym for "in":

connector(X, Y, in(X, Y)) -->
    [in] | [inside].

?- phrase(req(Req), [box, in, box]).
Req = in(box, box) ;
false.

?- phrase(req(Req), [box, inside, box]).
Req = in(box, box) ;
false.

Or we might want to treat "X under Y" as a synonym for "Y on X":

connector(X, Y, on(Y, X)) -->
    [under].

?- phrase(req(Req), [table, under, box]).
Req = on(box, table) ;
false.

Your question lacks of context, so I can't say if DCGs are really required for your task. In case, keep in mind that req//1 will compose badly, since p//1 will match anything, and only on backtracking the connectors will be recognized.

If connector is expected just once in a phrase, maybe this would be more readable:

req(L,in(X,Y)) :- append(X,[in|Y],L).
req(L,on(X,Y)) :- append(X,[on|Y],L).

edit

and easily generalized as you already have done:

req(L,C) :- append(X,[F|Y],L),conn(F),C=..[F,X,Y].
Related