3

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?
SigTerm
  • 26,089
  • 6
  • 66
  • 115
  • 1
    Related: [binary to number](http://stackoverflow.com/questions/4192063/reversible-binary-to-number-predicate?s=1|4.8836), [binary to decimal](http://stackoverflow.com/questions/27788739/binary-to-decimal-prolog?s=2|4.2336). – false May 20 '15 at 06:04
  • @false: If that interests you, the example is synthetic and is used for training. I.e. I wasn't interested in "how to convert string to number" but in "how to use dcgs in prolog as parsers". – SigTerm May 20 '15 at 06:20
  • But DCGs are exactly here to "convert" lists-of-something to something else - and vice versa. – false May 20 '15 at 12:43
  • @false: Meant to say that for string <-> number conversion, there's usually utility function. So the other guy might have been looking for that function, while I was looking for understanding of dcgs. Slightly different kind of question.... – SigTerm May 20 '15 at 12:49

1 Answers1

4

You're very close!

Commonly, a 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 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 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] ...
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • Can't believe I forgot about curly braces, because I already used them several times this morning. That explains arity of 4. The only minor issue is that `phrase(binaryNumber(X), "100")` apparently causes infinite loop if I press `;` after 1st result, but I think I can figure out fix for that myself. Thanks for the help. – SigTerm May 20 '15 at 06:19
  • @SigTerm: Here is a hint: the responsible code part is in plain sight (in the `binaryNumber//1` snippet that I copied and modified). And check out [tag:failure-slice]! – repeat May 20 '15 at 06:29
  • Well, I do realize that I need "termination" statement somewhere within second `binaryNumber` and that conidtion should use `fail` or ` ! ` or `\+` condition, but right now I can't figure out where it should be and what it should be. If I try inserting `{ ! },` after `binaryNumber(Next),`, statement stops working on strings longer than 2 characters and I can only make it work with reversed (which means that `110` is `3` and not `6`) numeric string: `digit(Cur), { ! }, binaryNumber(Next)` instead of `binaryNumber(Next), { ! }, digit(Cur)`. I suppose I might try again tomorrow. – SigTerm May 20 '15 at 07:18
  • 1
    @SigTerm. Try changing the order of `digit` and `binaryNumber` in the second clause. And if you use a new version of SWI-Prolog, make sure to start it up with option "--traditional"! – repeat May 20 '15 at 08:05
  • @false. So which kind of highlighting is permitted in code snippets? – repeat May 20 '15 at 08:06
  • @false: I also wonder about the highlighting. To me, highlighting (especially) `phrase/2` makes perfect sense in this answers, to draw attention to this very important interface predicate. Also the changed part in `{}/1` is worthy to stand out in my view. Exemplary answer by the way, +1! – mat May 20 '15 at 08:18
  • @SigTerm: Please avoid `!/0`, *especially* if you are just beginning to learn the language. Almost invariably, using `!/0` causes the loss of important declarative properties and severely restricts the ways in which you can use your predicates. You do not need any "termination statement" at all, and such a thing does not even exist under a declarative reading. – mat May 20 '15 at 08:22
  • @mat. I'm very happy with highlighting, it particularly helps my tired eyes. But I also know that over-using highlighting is bad. A uniform style of presentation is good, so I asked for guidelines. OTOH some variation has its appeal, too. – repeat May 20 '15 at 08:25
  • The point in this concrete case is that you (in my view exemplarily) emphasized the thing that was *omitted* in the original question, to draw OP's attention to this new feature of the code. Any advise on style should in my view take into account what is actually to be accomplished in such particular cases. – mat May 20 '15 at 08:30
  • "Try changing the order" That'll change direction of string, meaning least significant digit will be first from the left (right now it is 1st from the right). .... unless I'm missing something? – SigTerm May 20 '15 at 08:31
  • @SigTerm. You are right! Maybe we can use some other tools/techniques to tackle **that** (different) problem... – repeat May 20 '15 at 08:45
  • 1
    @repeat: Alright, fair enough. I'll post new question if I'll still need help with that problem (fairly sure I can just reverse the string by introducing "middleman" predicate). Thanks for the help/have fun. – SigTerm May 20 '15 at 08:48
  • 1
    @repeat: In this example, a comment would have been better. That is, if it can be expressed with comments to even better results, use comments. In a [tag:failure-slice], I do use `` and ``, but without using them, the code would be less readable, and with comments it would be much more bulky. – false May 20 '15 at 12:20
  • 1
    What about termination of `binaryNumber//1` looks quite non-terminating to me. (I did not look into details, I just miss some sensible restriction to the variables). – false May 20 '15 at 12:54
  • 1
    @mat: Such highlighting is very often deleted by "editors". It is difficult enough to justify its use for a [tag:failure-slice], but next to impossible in a case like this (which is a case very similar to other programming languages). – false May 20 '15 at 13:04
  • @false. Concerning the English text: What is a reasonable amount of highlighting there? And how much is too much? – repeat May 20 '15 at 14:49