Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog+clpfd: simple binary number parser with value

Tags:

prolog

dcg

clpfd

I'm currently trying to understand DCGs in prolog.

Consider this example.

digit(0) --> "0".
digit(1) --> "1".

binaryNumber(Val) --> digit(Val).

binaryNumber(Next*2 + Cur) -->
    %CurVal #= Cur + Next*2,
    binaryNumber(Next),
    digit(Cur).

That produces:

207 ?- binaryNumber(X, Y, []).
X = 0,
Y = [48] ;
X = 1,
Y = [49] ;
X = 0*2+0,
Y = [48, 48] ;
X = 0*2+1,
Y = [48, 49] ;
X = 1*2+0,
Y = [49, 48] ;
X = 1*2+1,
Y = [49, 49] ;
X = (0*2+0)*2+0,

Which is nice.

However, if I want to "convert" string to value:

:- use_module(library(clpfd)).

digit(0) --> "0".
digit(1) --> "1".

binaryNumber(Val) --> digit(Val).

binaryNumber(CurVal) -->
    CurVal #= Cur + Next*2,
    binaryNumber(Next),
    digit(Cur).

I get:

209 ?- binaryNumber(X, Y, []).
X = 0,
Y = [48] ;
X = 1,
Y = [49] ;
ERROR: binaryNumber/3: Undefined procedure: (#=)/4
ERROR:   However, there are definitions for:
ERROR:         (#=)/2
   Exception: (7) #=(_G4807345, _G4807428+_G4807431*2, _G4807346, _G4807475) ? 

...

Two questions:

  1. Why does binaryNumber want #= to have "arity" of 4?
  2. How do I fix this?
like image 268
SigTerm Avatar asked Nov 18 '25 22:11

SigTerm


1 Answers

You're very close!

Commonly, a dcg foo//n isn't implemented "directly", but by translating a grammar foo//n to a corresponding Prolog predicate foo//(n+2). This translation is done by term_expansion/2, a mechanism analogous to macros in other languages. Usually, you don't have to mind it at all.


For more on dcg read: (1) this DCG primer, and (2) the question "Is there a way or an algorithm to convert DCG into normal definite clauses in Prolog?" and the answers to that question.


Coming back to the subject, I see two issues in your dcg use:

  1. If used within grammar rules, "ordinary" Prolog goals must be encapsulated with curly braces {}/1, so they are skipped in aforementioned "grammar to predicate" translation step. In your code, you don't want to use (#=)//2 (a.k.a. (#=)/4), you want (#=)/2!

  2. It is good practise, not to use foo/(n+2) goals directly. Use phrase/2 or phrase/3 for that!

So let's edit the corresponding code snippet:

binaryNumber(Next*10 + Cur) -->
    { CurVal #= Cur + Next*2 },
    binaryNumber(Next),
    digit(Cur).

Now let's query!

?- phrase(binaryNumber(X),Ts).
X = 0, Ts = [48]    ;
X = 1, Ts = [49]    ;
X = 0, Ts = [48,48] ;
X = 1, Ts = [48,49] ;
X = 2, Ts = [49,48] ;
X = 3, Ts = [49,49] ...
like image 148
repeat Avatar answered Nov 22 '25 04:11

repeat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!