Playing around with DCGs and stubled upon the following problem:
I want to parse as well as produce an exact amount of spaces (" "
). My trivial approach of simply doing this:
trivial_nat_space(0) --> [].
trivial_nat_space(N) -->
{ N > 0, N is M+1 },
" ",
trivial_nat_space(M).
failed terribly, because of insufficient instantiation of N
and M
depending on whether i do
?- String=" ", my_string_codes(String,Codes), phrase(trivial_nat_space(Anz_Spaces), Codes, [])
or
?- Anz_Spaces=3,my_string_codes(String,Codes), phrase(trivial_nat_space(Anz_Spaces), Codes, [])
where (for convenience)
my_string_codes(S,C) :-
when((ground(S);ground(C)), string_codes(S,C)).
searching for a nice solution to the problem I made a version that depends on self defined nats:
z.
s(z).
s(s(O)) :-
s(O).
nat_num(S,C) :-
when((ground(S);ground(C)),nat_num_(S,C)).
nat_num_(z,0) :- !.
nat_num_(s(N),X) :-
nonvar(X),
!,
X > 0,
Y is X-1,
nat_num_(N,Y).
nat_num_(s(N),X) :-
var(X),
nat_num_(N,Y),
X is Y+1.
n_space(z) --> [].
n_space(s(N)) -->
" ",
n_space(N).
which I would like to avoid because the additional encoding of the natural number is kind of already present in the builtin numbers.
and this:
nat_space(0) --> [].
nat_space(N) -->
{ var(N) },
" ",
nat_space(M),
{ N is M+1 }.
nat_space(M) -->
{ nonvar(M), M>0 },
" ",
{ N is M-1 },
nat_space(N).
which does work fine:
?- Anz_Spaces=3,my_string_codes(String,Codes), once(phrase(nat_space(Anz_Spaces), Codes, [])).
Anz_Spaces = 3,
String = " ",
Codes = [32, 32, 32].
?- String=" ",my_string_codes(String,Codes), once(phrase(nat_space(Anz_Spaces), Codes, [])).
String = " ",
Codes = [32, 32, 32],
Anz_Spaces = 3.
However the encoding of nat_spaces
is (in my opinion) far from nice: it depends on meta-predicates to enforce a specific execution sequence, and (more seriously): if the parser were more complex than just " "
, the logic would have to be defined in a seperate DCG predicate/rule resulting in the logic for a single parser/generator to be split into two definitions (the separated one and the one enforcing the correct execution sequence).
Is this the canonical/standard way of solving problems like this or is there a more general, elegant solution that I am missing right now?
Additional Info:
I am using SWI-Prolog version 8.3.9 for x86_64-linux
with :- [library(dcg/basics)]
and no additional arguments when starting the runtime. Nor do I set any settings in the file with the definitions.
For your specific example, you can use CLP(fd) to be able to use the DCG in both ways:
trivial_nat_space(0) --> [].
trivial_nat_space(N) -->
{ N #> 0, M #= N-1 },
" ",
trivial_nat_space(M).
In the following sample runs I will use backticks (`
) to use coded strings:
?- phrase(trivial_nat_space(Anz_Spaces), ` `, []).
Anz_Spaces = 3 ;
false.
?- phrase(trivial_nat_space(3), Spaces, []).
Spaces = [32, 32, 32] ;
false.
?- phrase(trivial_nat_space(N), Spaces, []).
N = 0,
Spaces = [] ;
N = 1,
Spaces = [32] ;
N = 2,
Spaces = [32, 32] ;
N = 3,
Spaces = [32, 32, 32]
...