DCG version for maplist/3 - prolog

The following meta-predicate is often useful. Note that it cannot be called maplist//2, because its expansion would collide with maplist/4.
maplistDCG(_P_2, []) -->
[].
maplistDCG(P_2, [A|As]) -->
{call(P_2, A, B)},
[B],
maplistDCG(P_2, As).
There are several issues here. Certainly the name. But also the terminal [B]: should it be explicitly disconnected from the connecting goal?
Without above definition, one has to write either one of the following - both having serious termination issues.
maplistDCG1(P_2, As) -->
{maplist(P_2, As, Bs)},
seq(Bs).
maplistDCG2(P_2, As) -->
seq(Bs),
{maplist(P_2, As, Bs)}.
seq([]) -->
[].
seq([E|Es]) -->
[E],
seq(Es).

Does {call(P_2,A,B)}, [B] have advantages over [B], {call(P_2,A,B)}?
(And, if so, shouldn't maplist/3 get something like that, too?)
Let's put corresponding dcg and non-dcg variants side-by-side1:
dcg [B],{call(P_2,A,B)} and non-dcg Bs0 = [B|Bs], call(P_2,A,B)
maplistDCG(_,[]) --> []. % maplist(_,[],[]).
maplistDCG(P_2,[A|As]) --> % maplist(P_2,[A|As],Bs0) :-
[B], % Bs0 = [B|Bs],
{call(P_2,A,B)}, % call(P_2,A,B),
maplistDCG(P_2,As). % maplist(P_2,As,Bs).
dcg {call(P_2,A,B)},[B] and non-dcg call(P_2,A,B), Bs0 = [B|Bs]
maplistDCG(_,[]) --> []. % maplist(_,[],[]).
maplistDCG(P_2,[A|As]) --> % maplist(P_2,[A|As],Bs0) :-
{call(P_2,A,B)}, % call(P_2,A,B),
[B], % Bs0 = [B|Bs],
maplistDCG(P_2,As). % maplist(P_2,As,Bs).
Above, we highlighted the goal ordering in use now:
by maplist/3, as defined in this answer on SO and in the Prolog prologue
by maplistDCG//2, as defined in this question by the OP
If we consider that ...
... termination properties need to be taken into account and ...
... dcg and non-dcg variants should better behave the same2 ...
... we find that the variable should not be explicitly disconnected from the connecting goal. The natural DCG analogue of maplist/3 is maplistDCG//2 defined as follows:
maplistDCG(_,[]) -->
[].
maplistDCG(P_2,[A|As]) -->
[B],
{call(P_2,A,B)},
maplistDCG(P_2,As).
Footnote 1: To emphasize commonalities, we adapted variable names, code layout, and made some unifications explicit.
Footnote 2: ... unless we have really good reasons for their divergent behaviour ...

Related

Termination of prolog query using using dcgs

Given the program
foo([]) --> [].
foo([Start|Rest]) --> alphanum(Start), foo(Rest).
alphanum(Ch) --> [Ch], { char_type(Ch, alnum) }.
How can I make the query length(I, 2), phrase(foo(C), I), false. terminate?
I am using SWI-Prolog version 8.4.3 for x86_64-linux
The non-termination seems to be originating from the last dcg rule. With the following program (not what I want), the query terminates.
foo([]) --> [].
foo([Start|Rest]) --> alphanum(Start), foo(Rest).
alphanum(Ch) --> [Ch].
I don't mind any other formulation of the program that achieves the same results
It will terminate - but there's a lot of Unicode character combinations to loop through.
You probably want instead (note that this is using usually-preferable codes instead of chars):
foo([]) --> [].
foo([Start|Rest]) --> alnum(Start), foo(Rest).
alnum(Digit) --> [Digit], { between(0'0, 0'9, Digit) }.
alnum(Lower) --> [Lower], { between(0'a, 0'z, Lower) }.
alnum(Upper) --> [Upper], { between(0'A, 0'Z, Upper) }.
Result in swi-prolog:
?- length(I, 2), phrase(foo(C), I), writeln(I), false.
...
[90,88]
[90,89]
[90,90]
false.

DCG prolog returning multiple variable answers [duplicate]

If I have the below piece of code, how would I make it produce Answer= 5 and Answer2= 10?. I run the goal ?- test(Data),lpsolve(Data, [Answer1,Answer2]).
:-use_module(library(clpfd)).
test([the, variable, X, is, five,fullstop,
the,variable, Y, is, ten, fullstop]).
lpsolve(Data, [Answer,Answer2]):- sentence(Answer, Data,[]).
sentence(X) --> nounphrase, verbphrase(X).
nounphrase --> [the], [variable].
verbphrase(X) --> [X], [is], [five],[fullstop], {X = 5}.
sentence(Y) --> nounphrase, verbphrase(Y).
nounphrase --> [the], [variable].
verbphrase(Y) --> [Y], [is], [ten],[fullstop], {Y = 10}.
Example of a program that actually runs and is closely related is the following:
:-use_module(library(clpfd)).
test([the, variable, X, is, five,fullstop]).
lpsolve(Data, Answer):- sentence(Answer, Data,[]).
sentence(X) --> nounphrase, verbphrase(X).
nounphrase --> [the], [variable].
verbphrase(X) --> [X], [is], [five],[fullstop], {X = 5}.
I have just one sentence to test and the goal succeeds as shown below.
?- test(Data),lpsolve(Data, Answer).
Data = [the, variable, 5, is, five],
Answer = 5.
EDIT
I try the following as per the first comment:
:-use_module(library(clpfd)).
test([the, variable, x, is, five,fullstop,
the,variable, y, is, ten, fullstop]).
lpsolve(Data, Answer):- sentence(Answer, Data,[]).
sentence(X) --> nounphrase, verbphrase(X).
nounphrase --> [the], [variable].
verbphrase(X) --> [x], [is], [five],[fullstop], {X = 5}.
verbphrase(Y) --> [y], [is], [ten],[fullstop], {Y = 10}.
I get the following:
-? test(Data),lpsolve(Data, Answer).
false.
I'm not really sure what you're trying to do here, but I feel your DCG is broken down in completely strange ways and you may benefit from seeing another way to arrange it.
You have a list of variable bindings, so you should already be thinking in terms of obtaining a list of results rather than a single result:
sentences([S|Ss]) --> sentence(S), sentences(Ss).
sentences([]) --> [].
What is a sentence? It is a noun phrase and a verb phrase, in your simple system. But you have broken the noun and verb phrases apart incorrectly for English, where a sentence like "The variable X is 5" should be broken into a noun phrase subject "The variable X" and a verb phrase "is 5". Ideally, the verb phrase should be decomposed further into a verb another noun phrase, but we're ignoring that detail for now. I am looking for the verb "is" to relate it to the Prolog predicate =/2, and handling fullstop here:
sentence(N1=N2) --> nounphrase(N1), verbphrase(is, N2), [fullstop].
OK, so now we need your noun phrase:
nounphrase(N) --> [the, variable, N].
And your verb phrase:
verbphrase(is, V) --> [is], value(V).
I'm only handling the two decodings and I'm doing it implicitly in the DCG definition:
value(5) --> [five].
value(10) --> [ten].
You'll find this works for the use case you've defined above:
?- phrase(sentences(S), [the,variable,X,is,five,fullstop,the,variable,Y,is,ten,fullstop]).
S = [X=5, Y=10] ;
false.

Using list in Prolog DCG

I am trying to convert a Prolog predicate into DCG code. Even if I am familiar with grammar langage I have some troubles to understand how DCG works with lists and how I am supposed to use it.
Actually, this is my predicate :
cleanList([], []).
cleanList([H|L], [H|LL]) :-
number(H),
cleanList(L, LL),
!.
cleanList([_|L], LL) :-
cleanList(L, LL).
It is a simple predicate which removes non-numeric elements.
I would like to have the same behaviour writes in DCG.
I tried something like that (which does not work obviously) :
cleanList([]) --> [].
cleanList([H]) --> {number(H)}.
cleanList([H|T]) --> [H|T], {number(H)}, cleanList(T).
Is it possible to explain me what is wrong or what is missing ?
Thank you !
The purpose of DCG notation is exactly to hide, or better, make implicit, the tokens list. So, your code should look like
cleanList([]) --> [].
cleanList([H|T]) --> [H], {number(H)}, cleanList(T).
cleanList(L) --> [H], {\+number(H)}, cleanList(L).
that can be made more efficient:
cleanList([]) --> [].
cleanList([H|T]) --> [H], {number(H)}, !, cleanList(T).
cleanList(L) --> [_], cleanList(L).
A style note: Prologgers do prefers to avoid camels :)
clean_list([]) --> [].
etc...
Also, I would prefer more compact code:
clean_list([]) --> [].
clean_list(R) --> [H], {number(H) -> R = [H|T] ; R = T}, clean_list(T).

In Prolog DCGs, how to remove over general solutions?

I have a text file containing a sequence. For example:
GGGGGGGGAACCCCCCCCCCTTGGGGGGGGGGGGGGGGAACCCCCCCCCCTTGGGGGGGG
I have wrote the following DCG to find the sequence between AA and TT.
:- use_module(library(pio)).
:- use_module(library(dcg/basics)).
:- portray_text(true).
process(Xs) :- phrase_from_file(find(Xs), 'string.txt').
anyseq([]) -->[].
anyseq([E|Es]) --> [E], anyseq(Es).
begin --> "AA".
end -->"TT".
find(Seq) -->
anyseq(_),begin,anyseq(Seq),end, anyseq(_).
I query and I get:
?- process(Xs).
Xs = "CCCCCCCCCC" ;
Xs = "CCCCCCCCCCTTGGGGGGGGGGGGG...CCCCC" ;
Xs = "CCCCCCCCCC" ;
false.
But I dont want it to find the second solution or ones like it. Only the solutions between one pair of AA and TTs not all combinations. I have a feeling I could use string_without and string in library dcg basiscs but I dont understand how to use them.
your anyseq//1 is identical to string//1 from library(dcg/basics), and shares the same 'problem'.
To keep in control, I would introduce a 'between separators' state:
elem(E) --> begin, string(E), end, !.
begin --> "AA".
end -->"TT".
find(Seq) -->
anyseq(_),elem(Seq).
anyseq([]) -->[].
anyseq([E|Es]) --> [E], anyseq(Es).
process(Xs) :-
phrase(find(Xs), `GGGGGGGGAACCCCCCCCCCTTGGGGGGGGGGGGGGGGAACCCCC+++CCCCCTTGGGGGGGG`,_).
now I get
?- process(X).
X = "CCCCCCCCCC" ;
X = "CCCCC+++CCCCC" ;
false.
note the anonymous var as last argument of phrase/3: it's needed to suit the change in 'control flow' induced by the more strict pattern used: elem//1 is not followed by anyseq//1, because any two sequences 'sharing' anyseq//1 would be problematic.
In the end, you should change your grammar to collect elem//1 with a right recursive grammar....
First, let me suggest that you most probably misrepresent the problem, at least if this is about mRNA-sequences. There, bases occur in triplets, or codons and the start is methionine or formlymethionine, but the end are three different triplets. So most probably you want to use such a representation.
The sequence in between might be defined using all_seq//2, if_/3, (=)/3:
mRNAseq(Cs) -->
[methionine],
all_seq(\C^maplist(dif(C),[amber,ochre,opal]), Cs),
( [amber] | [ochre] | [opal]).
or:
mRNAseq(Cs) -->
[methionine],
all_seq(list_without([amber,ochre,opal]), Cs),
( [amber] | [ochre] | [opal]).
list_without(Xs, E) :-
maplist(dif(E), Xs).
But back to your literal statement, and your question about declarative names. anyseq and seq mean essentially the same.
% :- set_prolog_flag(double_quotes, codes). % pick this
:- set_prolog_flag(double_quotes, chars). % or pick that
... --> [] | [_], ... .
seq([]) -->
[].
seq([E|Es]) -->
[E],
seq(Es).
mRNAcontent(Cs) -->
...,
"AA",
seq(Cs),
"TT",
{no_TT(Cs)}, % restriction
... .
no_TT([]).
no_TT([E|Es0]) :-
if([E] = "T",
( Es0 = [F|Es], dif([F],"T") ),
Es0 = Es),
no_TT(Es).
The meaning of no_TT/1 is: There is no sequence "TT" in the list, nor a "T" at then end. So no_TT("T") fails as well, for it might collide with the subsequent "TT"!
So why is it a good idea to use pure, monotonic definitions? You will most probably be tempted to add restrictions. In a pure monotonic form, restrictions are harmless. But in the procedural version suggested in another answer, you will get simply different results that are no restrictions at all.

How to display solution?

I am trying to create a program that displays a solution to a problem and I need a way to display the solution that it created. I have two operations that can be used to solve the problem though and the order in which they are called matters.
test(a) :- write('use a ').
test(b) :- write('use b '), fail.
test(c) :- test(a), test(b), test(a).
test(c) :- test(a), test(a).
Please note this is an example and should not be taken literally. Think of test(a) and test(b) as the operations and test(c) as the function that's checking which order would be valid.
Now test(c). will print even the ones that fails. Resulting in output "use a use b use a use a".
Instead of using side effects, consider using a DCG that describes the list of goals. Your example can be easily turned into a DCG with a few mechanical rewriting steps, for example:
test(a) --> ['use a'].
test(b) --> ['use b'], { false }.
test(c) --> test(a), test(b), test(a).
test(c) --> test(a), test(a).
Then you can query:
?- phrase(test(c), Ls).
Ls = ['use a', 'use a'].
When you have such a list Ls, you can easily write it in any way you want. Notice though that the toplevel already prints each solution in a useful way, and there may be little reason to do so yourself.
Your problem seems a legitimate call for assertz/retract.
These builtins allow to track the chronological sequence, and to store arbitrary Prolog terms, then you could write
:- dynamic results/2.
test(a) :- a_code(Result), assertz(results(a, Result)).
test(b) :- b_code(Result), assertz(results(b, Result)).
test(c) :- test(a), test(b), test(a).
test(c) :- test(a), test(a).
check_results :- forall(retract(Name, Result), writeln(Name = Result)).
If you want to track failed executions, you can extend that schema:
test(a) :- a_code(Result) -> assertz(results(ok(a), Result)) ; assertz(results(ko(a), _)).
test(b) :- b_code(Result) -> assertz(results(ok(b), Result)) ; assertz(results(ko(b), _)).

Resources