Prolog Roman Numerals (Attribute Grammars)

2019-06-25 12:37发布

I am working on an assignment in that scans a list of numerals and should return whether the list is a valid roman numeral and the decimal value of the numerals. Ex)

1 ?- roman(N, ['I'], []).
N = 1
true.

2 ?- 

When I run the program that I feel should work, the decimal value is always right, so I'm guessing I got the synthesized attributes part right, but it always returns false for numeral lists that should return true. I'd also like to add that it aborts when it is supposed to if more than 3 Is, Xs, or Cs are present.

1 ?- roman(N, ['I'], []).
N = 1 ;
false.

2 ?- roman(N, ['I','I','I','I'], []).
Error: too many I's
% Execution Aborted
3 ?- 

When I take out the N and throw in a {write('N = '), write(N)}, it works fine and returns true.

1 ?- roman(['I'], []).
N = 1
true.

When I remove {N is ValH + ValT + ValU} it returns true, however, it no longer displays the decimal value. Here is the top line of my code (because this is a current assignment, I would prefer to show as little as is necessary to get an answer):

roman(N) --> hundreds(ValH), tens(ValT), units(ValU), {N is ValH + ValT + ValU}.

Why is this returning false with N, but true without, and how do I fix it?

Assignment: The following BNF specification defines the language of Roman numerals less than 1000:

<roman> ::= <hundreds> <tens> <units>
<hundreds> ::= <low hundreds> | CD | D <low hundreds> | CM
<low hundreds> ::= e | <low hundreds> C
<tens> ::= <low tens> | XL | L <low tens> | XC
<low tens> ::= e | <low tens> X
<units> ::= <low units> | IV | V <low units> | IX
<low units> ::= e | <low units> I

Define attributes for this grammar to carry out two tasks:

a) Restrict the number of X’s in <low tens>, the I’s in <low units>, and the C’s in <low hundreds> to no more than three.

b) Provide an attribute for <roman> that gives the decimal value of the Roman numeral being defined.

Define any other attributes needed for these tasks, but do not change the BNF grammar.

1条回答
聊天终结者
2楼-- · 2019-06-25 13:27

Did you noticed the grammar is formed of the same pattern (group//5) repeated 3 times, just with different symbols ? I like the compactness...

roman(N) -->
    group('C','D','M',100, H),
    group('X','L','C',10, T),
    group('I','V','X',1, U),
    {N is H+T+U}.
group(A,B,C, Scale, Value) -->
    (   g3(A, T)
    ;   [A, B], {T = 4}
    % thanks to Daniel and Will for catching bugs
    ;   [B], g3(A, F), {T is 5+F}
    ;   [B], {T is 5}
    ;   [A, C], {T = 9}
    ;   {T = 0}
    ),  {Value is Scale * T}.


g3(C, 1) --> [C].
g3(C, 2) --> [C,C].
g3(C, 3) --> [C,C,C].

some test

?- atom_chars('CMXXX',L), phrase(roman(N),L).
L = ['C', 'M', 'X', 'X', 'X'],
N = 930 ;
false.

?- atom_chars('CMXLVIII',L), phrase(roman(N),L).
L = ['C', 'M', 'X', 'L', 'V', 'I', 'I', 'I'],
N = 943 ;
false.

Just a curiousity, showing DCG at work...

edit after Daniel and Will comments...

?- atom_chars('VIII',L), phrase(roman(N),L).
L = ['V', 'I', 'I', 'I'],
N = 8 .

?- phrase(roman(X), ['L','I','X']).
X = 59 .
查看更多
登录 后发表回答