In grammar rules (dcg), there are several predefined constructs: (',')//2 meaning concatenation, ('|')//2 meaning alternation etc. One construct which is supported by several but not all Prolog systems is (\+)//1.
Personally, I have used it only for the sake of using it. I have never seen it in code written by others.
So, are there legitimate uses of (\+)//1?
Edit: And additionally, are there legitimate uses of (\+)//1 within a query phrase(nt, L) with L an uninstantiated variable.
\+ can be used to create grammars that are less ambiguous.
The advantage of using \+ over ! for example, is a certain
declarativity of \+, so that for example the resulting DCG
rules can be reordered.
Lets make an example, consider the following grammar:
s([X|Y]) --> t(X), s(Y). % 1
s([]) --> []. % 2
t(2) --> [a,a]. % 3
t(1) --> [a]. % 4
The above grammar is highly ambiguous, for example I get multiple
parses for the following input:
?- phrase(s(A),[a,a,a,a,a]).
A = [2,2,1] ;
A = [2,1,2] ;
A = [2,1,1,1] ;
etc..
Now assume I want to prefer the long parse of t over the
short parse of t. I can do this with a cut as follows:
t(2) --> [a,a], !. % 5
t(1) --> [a]. % 6
?- phrase(s(A),[a,a,a,a,a]).
A = [2,2,1] ;
No
Unfortunately I cannot reorder. Since doing the following
does not give the desired result. Although s(A) now yields
the results in a different order, we are back to square one,
since the grammar is ambiguous again:
t(1) --> [a]. % 7
t(2) --> [a,a], !. % 8
?- phrase(s(A),[a,a,a,a,a]).
A = [1,1,1,1,1] ;
A = [1,1,1,2] ;
A = [1,1,2,1] ;
etc...
Now lets try the same with \+. We can replace the cut
by the following negation:
t(2) --> [a,a]. % 9
t(1) --> [a], \+ [a]. % 10
?- phrase(s(A),[a,a,a,a,a]).
A = [2,2,1] ;
No
Now lets try whether we can reorder. We reorder the
grammar rules of t//1:
t(1) --> [a], \+ [a]. % 11
t(2) --> [a,a]. % 12
?- phrase(s(A),[a,a,a,a,a]).
A = [2,2,1] ;
No
The declarativity is very useful. It means for example
that we can use \+ in a right-to-left chart parser that
picks the grammar rules in an arbitrary order. The
declarativity assures that the bottom up forward chaining
of the chart parser yields the same result independently of
the input order of the DCG rules.
It is then possible to apply the DCG technique in large
natural language (NL) projects and it scales well. The
NL grammars can be empirically tuned into determinism.
The more deterministic a grammar the more efficient
its parsing. Complex NL grammars that are otherwise
intractable become feasible.
Bye
When L is not instantiated then the grammar is used for text generation. Then
you don't need the grammar \+ at all. Since there is no more problem of ambiguity
from some text.
Lets make an example, consider the following grammar:
s([X|Y]) --> t(X), s(Y). % 1
s([]) --> []. % 2
t(2) --> [a,a]. % 3
t(1) --> [a]. % 4
Each different parse has a different parse tree. And there is no ambiguity in
using phrase/2 to generate text. The following queries give exactly one
answer:
?- phrase(s([2,1]),L).
L = [a,a,a]
?- phrase(s([1,2]),L).
L = [a,a,a]
?- phrase(s([1,1,1]),L).
L = [a,a,a]
But there is a little reuse problem. Assume I have a NL grammar with \+ for
parsing. I then cannot use it for unparsing. Since the instantiation pattern
of the \+ goal will be different, and therefore the semantic of the construct
changes.
The way out is probably just to use two grammars. One for parsing and one for
unparsing. I guess parsing and unparsing are two different cognitive capabilities.
Remember in school, there were reading exercises and writting exercises. The
same happens in computer science.
I think it is also in some cases possible to use a single grammar, and view \+ as
an annotation for disambiguation, that is dropped during unparse or differently
handled. One could build such a mechanism. But the issues with unparsing versus
parsing are deeper: Bidirectionallity of auxiliary conditions ({}/1), left recursion
during unparsing, etc...
Bye
Related
I have a simple grammar, which takes 3 list items and runs a different dcg rule on each.
[debug] ?- phrase(sentence(X), [sky, a, 1], []).
X = [bright, amber, on] .
Code:
sentence([A,C,R]) -->
analyse(A),
colour(C),
rating(R).
analyse(bright) --> [sky].
analyse(dark) --> [cave].
colour(red) --> [r].
colour(amber) --> [a].
colour(green) --> [g].
rating(on) --> [1].
rating(off) --> [0].
This works fine.
My problem is that my input list needs needs to have 2 items, not 3, and the second atom is a concat atom of colour and rating:
[sky, a1]
So somehow I have to (?) split this atom into [a, 1] and then the colour and rating rules will work with a simple dcg rule.
I can't work out how to do this..obviously with normal prolog, I'd just use atom_chars, but I can't work out how to interleave this with the grammar.
In a perfect world, it feels like I should not have to resort to using atom_chars, and I should be able to come up with a simple dcg rule to split it, but I'm not sure if this is possible, since we are parsing lists, not atoms.
As you have said yourself, you just need to use a predicate like atom_chars/2. You can interleave normal code into a DCG rule by enclosing it in { and }.
But there is something fishy about your problem definition. As you have also said yourself, you are parsing a list, not an atom. The list you are parsing should be already properly tokenized, otherwise you cannot expect to define a DCG that can parse it. Or am I seeing this wrong?
So in other words: you take your input, split into single chars, tokenize that using a DCG. Depending on your input, you can do the parsing in the same step.
It was clear that a refined DCG rule could work, but, alas, it took too much time to me to craft a solution for your problem.
Here it is:
sentence([A,C,R]) -->
analyse(A),
colour(C),
rating(R).
analyse(bright) --> [sky].
analyse(dark) --> [cave].
colour(red) --> [r].
colour(amber) --> [a].
colour(green) --> [g].
colour(X), As --> [A], {
atom_codes(A, Cs),
maplist(char2atomic, Cs, L),
phrase(colour(X), L, As)}.
rating(on) --> [1].
rating(off) --> [0].
char2atomic(C, A) :- code_type(C, digit) -> number_codes(A, [C]) ; atom_codes(A, [C]).
yields
?- phrase(sentence(X), [sky, a1], []).
X = [bright, amber, on]
the key it's the use of 'pushback' (i.e. colour(X), As -->...).
Here we split the unparsable input, consume a token, and push back the rest...
As usual, most of time was required to understand where my first attempt failed: I was coding char2atomic(C, A) :- atom_codes(A, [C])., but then rating//1 failed...
I am trying to define a palindrome where the number of a's is one less than the number of b's.
I cant seem to figure out how to write it properly
please-->palindromes.
palindromes-->[].
palindromes-->[a].
palindromes-->[b].
palindromes--> [b],palindromes,[b].
Think about this: where the surplus 'b' could stay ? In a palindrome, there is only one such place. Then change the symmetric definition, that in BNF (you already know as translate to DCG) would read
S :: P
P :: a P a | b P b | {epsilon}
You're on the right track, you just need a way to deal with the difference in counts. You can do this by adding a numeric argument to your palindromes grammar term.
First I'll define an ordinary Prolog rule implementing "B is two more than A":
plus2(A,B) :- number(A), !, B is A+2.
plus2(A,B) :- number(B), !, A is B-2.
plus2(A,B) :- var(A), var(B), throw(error(instantiation_error,plus2/2)).
Then we'll say palindromes(Diff) means any palindrome on the given alphabet where the number of b letters minus the number of a letters is Diff. For the base cases, you know Diff exactly:
palindromes(0) --> [].
palindromes(-1) --> [a].
palindromes(1) --> [b].
For the recursive grammar rules, we can use a code block in {braces} to check the plus2 predicate:
palindromes(DiffOuter) --> [b], palindromes(DiffInner), [b],
{ plus2(DiffInner, DiffOuter) }.
palindromes(DiffOuter) --> [a], palindromes(DiffInner), [a],
{ plus2(DiffOuter, DiffInner) }.
To finish off, the top-level grammar rule is simply
please --> palindromes(1).
I am trying to understand prolog and definite clause grammar but I am having a very hard time understanding both of them.
I am really trying to understand how to use the dcg syntax...
Here I have two examples:
The first is actually the code from another question on this forum but with an additional question:
The code looked like this:
s --> first, operator, second.
first --> [X].
operator --> ['+'].
second --> [X].
And when Prolog is asked about this, it returns true/false but I can't for the life of me figure out how to actually modify this to "bind" the value so if asked s(X, [2,+,2],[]). it would return the value of first, so instead of returning true it'd say X = 2
Anyway back to the actual question. I have a few rules in normal prolog and this is one of them; it doesn't actually do anything and was merely made up as an example.
do(X, Y, [H|T], Sum):-
H == 1, %check if H is 1
X = H,
Y = T,
Additional is H+5,
Sum is Additional+Additional.
Basically, I am asking if someone could translate this to DCG so that I could try and understand the basic syntax of DCG! I've tried reading some tutorials but I feel like I haven't gotten any wiser...
DCG:
foo(A1, A2, A3, ... , An) --> bar.
Prolog:
foo(A1, A2, A3, ... , An, X, Y) :- bar(X,Y)
So, s should be changed to:
s(X) --> first(X), operator, second.
first(X) --> [X].
operator --> ['+'].
second --> [X].
Of course, it might be better to return the actual result; to do this you should encapsulate prolog code in the DCG clause which is done with {}:
s(Z) --> first(X), operator, second(Y), {Z is X+Y}.
first(X) --> [X].
operator --> ['+'].
second(X) --> [X].
(naturally, if you have more operators, the prolog code won't be that simple).
Regarding the do/4 predicate, it should be something like this:
do(X,Y,[H|T],Sum) -->
{H == 1, %check if H is 1
X = H,
Y = T,
Additional is H+5,
Sum is Additional+Additional}.
but I don't see why you would want that.
One last tip: it's recommended to use phrase/3 instead of adding the last two arguments in a DCG predicate.
it's not easy to translate do/4 to DCG in meaningful way. I've removed arguments that 'copy' the hidden arguments of the DCG.
do(Sum) -->
[1], %check if H is 1
{ % braces allow 'normal' Prolog code (but we have no access to 'hidden' arguments)
Additional is H+5,
Sum is Additional+Additional
}.
edit sorry I forgot H in Additional is H+5,, should read Additional is 1+5,...
I have a manually made DCG rule to select idiomatic phrases
over single words. The DCG rule reads as follows:
seq(cons(X,Y), I, O) :- noun(X, I, H), seq(Y, H, O), \+ noun(_, I, O).
seq(X) --> noun(X).
The first clause is manually made, since (:-)/2 is used instead
of (-->)/2. Can I replace this manually made clause by
some clause that uses standard DCG?
Best Regards
P.S.: Here is some test data:
noun(n1) --> ['trojan'].
noun(n2) --> ['horse'].
noun(n3) --> ['trojan', 'horse'].
noun(n4) --> ['war'].
And here are some test cases, the important test case is the first test case, since it does only
deliver n3 and not cons(n1,n2). The behaviour of the first test case is what is especially desired:
?- phrase(seq(X),['trojan','horse']).
X = n3 ;
No
?- phrase(seq(X),['war','horse']).
X = cons(n4,n2) ;
No
?- phrase(seq(X),['trojan','war']).
X = cons(n1,n4) ;
No
(To avoid collisions with other non-terminals I renamed your seq//1 to nounseq//1)
Can I replace this manually made clause by some clause that uses standard DCG?
No, because it is not steadfast and it is STO (details below).
Intended meaning
But let me start with the intended meaning of your program. You say you want to select idiomatic phrases over single words. Is your program really doing this? Or, to put it differently, is your definition really unique? I could now construct a counterexample, but let Prolog do the thinking:
nouns --> [] | noun(_), nouns.
?- length(Ph, N), phrase(nouns,Ph),
dif(X,Y), phrase(nounseq(X),Ph), phrase(nounseq(Y),Ph).
Ph = [trojan,horse,trojan], N = 3, X = cons(n1,cons(n2,n1)), Y = cons(n3,n1)
; ...
; Ph = [trojan,horse,war], N = 3, X = cons(n3,n4), Y = cons(n1,cons(n2,n4))
; ... .
So your definition is ambiguous. What you essentially want (probably) is some kind of rewrite system. But those are rarely defined in a determinate manner. What, if two words overlap like an additional noun(n5) --> [horse, war]. etc.
Conformance
A disclaimer up-front: Currently, the DCG document is still being developed — and comments are very welcome! You find all material in this place. So strictly speaking, there is at the current point in time no notion of conformance for DCG.
Steadfastness
One central property a conforming definition must maintain is the property of steadfastness. So before looking into your definition, I will compare two goals of phrase/3 (running SWI in default mode).
?- Ph = [], phrase(nounseq(cons(n4,n4)),Ph0,Ph).
Ph = [], Ph0 = [war,war]
; false.
?- phrase(nounseq(cons(n4,n4)),Ph0,Ph), Ph = [].
false.
?- phrase(nounseq(cons(n4,n4)),Ph0,Ph).
false.
Moving the goal Ph = [] at the end, removes the only solution. Therefore, your definition is not steadfast. This is due to the way how you handle (\+)/1: The variable O must not occur within the (\+)/1. But on the other hand, if it does not occur within (\+)/1 you can only inspect the beginning of a sentence. And not the entire sentence.
Subject to occurs-check property
But the situation is worse:
?- set_prolog_flag(occurs_check,error).
true.
?- phrase(nounseq(cons(n4,n4)),Ph0,Ph).
ERROR: noun/3: Cannot unify _G968 with [war|_G968]: would create an infinite tree
So your program relies on STO-unifications (subject-to-occurs-check unifications) whose outcome is explicitly undefined in
ISO/IEC 13211-1 Subclause 7.3.3 Subject to occurs-check (STO) and not subject to occurs-check (NSTO)
This is rather due to your intention to define the intersection of two non-terminals. Consider the following way to express it:
:- op( 950, xfx, //\\). % ASCII approximation for ∩ - 2229;INTERSECTION
(NT1 //\\ NT2) -->
call(Xs0^Xs^(phrase(NT1,Xs0,Xs),phrase(NT2,Xs0,Xs))).
% The following is predefined in library(lambda):
^(V0, Goal, V0, V) :-
call(Goal,V).
^(V, Goal, V) :-
call(Goal).
Already with this definition we can get into STO situations:
?- phrase(([a]//\\[a,b]), Ph0,Ph).
ERROR: =/2: Cannot unify _G3449 with [b|_G3449]: would create an infinite tree
In fact, when using rational trees we get:
?- set_prolog_flag(occurs_check,false).
true.
?- phrase(([a]//\\[a,b]), Ph0,Ph).
Ph0 = [a|_S1], % where
_S1 = [b|_S1],
Ph = [b|_S1].
So there is an infinite list which certainly has not much meaning for natural language sentences (except for persons of infinite resource and capacity...).
I'm trying to write some dcg grammar in prolog which will describe language of
a^nb^n n>=0
"",ab,aabb,aaabbb itd
All what I wrote is
s --> slowo.
slowo --> [a],slowo,[b],!.
slowo --> [].
And its good as long as all I want to do is just check if word is correct, but how should dcg grammar look in prolog for ?-phrase(s,X) which will generate all words from my language?
In addition to #Mog's answer, let us consider the more general case:
What, if the grammar is so complex, that reordering DCG-rules will not help? How can we still enumerate all sentences? If the grammar is formulated in such a manner, that it terminates for a fixed length, we get all sentences with
?- length(Xs, N), phrase(s, Xs).
The goal length alone will enumerate all lists in a fair manner. That is, starting with the shortest [] all lists are enumerated:
?- length(Xs, N).
Xs = [], N = 0
; Xs = [_A], N = 1
; Xs = [_A,_B], N = 2
; Xs = [_A,_B,_C], N = 3
; Xs = [_A,_B,_C,_D], N = 4
; ... .
And now, the length being fixed, the goal phrase(s, Xs) will find all answers for that fixed length. As an example, look at Mat's answer to this grammar.
So this is a general method to inspect a grammar's sentences. However - there is a price to pay for this generality! For grammars with finitely many sentences, out method will not terminate:
:- use_module(library(double_quotes)).
s --> "a"|"b".
?- phrase(s, Xs).
Xs = "a"
; Xs = "b".
This grammar works out of the box, but together with length/2 we get now:
?- length(Xs, N),phrase(s, Xs).
Xs = "a", N = 1
; Xs = "b", N = 1
; loops.
This method is often called iterative deepening, although this term more precisely imposes a bound to the depth of the derivation. But we are imposing a bound to the actual "output". So iterative deepening is also able to handle left-recursion, whereas length/2 works only for grammars that terminate for a fixed input length.
What makes this technique so particularly interesting in Prolog is that it meshes perfectly with Prolog's chronological backtracking mechanism.
If you're beginning with Prolog, try to avoid the use of !/0. You can generally do better without it.
Here for example, your grammar could be written as follows:
s --> [].
s --> [a], s, [b].
and queried as follows:
?- phrase(s, X).
Note that prolog clauses are picked from left to right and top to bottom, so a rule written at the top of another one will be prioritized when the backtracking is involved.
In SWI Prolog, I could use:
s(X, []).
or
phrase(s, X).
(as you suggested) to get all of the strings. In order for that to produce any answers without a stack overflow, however, I needed to reverse the order of the two rules for slowo and remove the cut from the recursive rule.