[fpc-pascal] Re: StrUtils.RomanToInt oddities

Alberto Narduzzi albertonarduzzi at yahoo.com
Mon Sep 23 21:13:33 CEST 2013


Premise: I didn't go through your entire implementation, thou' at first 
sight looks much better than the current one.

I know roman numerals since I was 8 or 9 yo, that makes a quick and dirt 
result of about 35 years.

Thing is:

1. in the roman numerals, not all the digits can be subtracted from the 
others
2. no more than three same roman numerals can appear in a row.

so that XM is as invalid as CCCC.
(although this amenity can be seen on some - in my opinion poor - 
wristwatches' face that uses roman numerals, for the 4h, showing IIII 
instead of the correct IV, but this is another story)

in roman numerals we have only these digits:

I(1)   V(5)
X(10)  L(50)
C(100) D(500)
M(1000) (sorry, no digit for the expected progression, for 5000)

the only digits that can be subtracted are I, X and C (only those on the 
left side of the "table" above), and they can only from the 2 digits 
immediately following them in the system, so that:

I can be subtracted only from V and X
X can be subtracted only from L and C
C can be subtracted only from D and M

so that 45 cannot be written as VL (50 - 5, as one might suspect) but 
must be written as XLV, or to better understand, as (XL)V = 40 + 5

The maximum roman numeral that can be represented is 4999, that is NOT 
MMMIM, but instead: MMM(CM)(XC)(IX) 
1000+1000+1000+(1000-900)+(100-10)+(10-1)

After this, roman numerals always begin with bigger numbers to sum up, 
and proceed to smaller, so you cannot start adding up 10, then 500, then 
again 1; like XDI (whether it be intended to be 491 or 511 - just no way)

I did an implementation some time ago, for pure fun, and provided I find 
it out (or maybe I can just rewrite it, on the basis of this knowledge) 
I will be - repository allowing ;-) - wery happy to contribute with my 
2c for it.


Cheers, A.


On 20/09/13 21:49, Bart wrote:
> On 9/20/13, Reinier Olislagers<reinierolislagers at gmail.com>  wrote:
>
>> The question however becomes "what is the
>> algorithm for deciding invalid characters" which IMO will become a mess
>> very quickly. Much better to just consider the entire input as invalid.
>>
>
> Here's my implementation:
>
> program test;
>
> {$mode objfpc}
> {$H+}
>
> uses
>    SysUtils, StrUtils;
>
>
> function TryRomanToInt(S: String; out N: Integer): Boolean;
> var
>    i, Len: Integer;
>    Terminated: Boolean;
> begin
>    Result := (False);
>    S := UpperCase(S);  //don't use AnsiUpperCase please
>    Len := Length(S);
>    if (Len = 0) then Exit;
>    i := 1;
>    N := 0;
>    Terminated := False;
>    //leading M's
>    while (i<= Len) and (S[i] = 'M') do
>    begin
>      //writeln('TryRomanToInt: Found 1000');
>      Inc(i);
>      N := N + 1000;
>    end;
>    //then CM or or CD or D or (C, CC, CCC, CCCC)
>    if (i<= Len) and (S[i] = 'D') then
>    begin
>      //writeln('TryRomanToInt: Found 500');
>      Inc(i);
>      N := N + 500;
>    end
>    else if (i + 1<= Len) and (S[i] = 'C') then
>    begin
>      if (S[i+1] = 'M') then
>      begin
>        //writeln('TryRomanToInt: Found 900');
>        Inc(i,2);
>        N := N + 900;
>      end
>      else if (S[i+1] = 'D') then
>      begin
>        //writeln('TryRomanToInt: Found 400');
>        Inc(i,2);
>        N := N + 400;
>      end;
>    end ;
>    //next max 4 C's
>    if (i<= Len) and (S[i] = 'C') then
>    begin
>      //find max 4 C's
>      //writeln('TryRomanToInt: Found 100');
>      Inc(i);
>      N := N + 100;
>      if (i<= Len) and (S[i] = 'C') then
>      begin
>        //writeln('TryRomanToInt: Found 100');
>        Inc(i);
>        N := N + 100;
>      end;
>      if (i<= Len) and (S[i] = 'C') then
>      begin
>        //writeln('TryRomanToInt: Found 100');
>        Inc(i);
>        N := N + 100;
>      end;
>      if (i<= Len) and (S[i] = 'C') then
>      begin
>        //writeln('TryRomanToInt: Found 100');
>        Inc(i);
>        N := N + 100;
>      end;
>    end;
>
>    //then XC or XL
>    if (i + 1<= Len) and (S[i] = 'X') then
>    begin
>      if (S[i+1] = 'C') then
>      begin
>        //writeln('TryRomanToInt: Found 90');
>        Inc(i,2);
>        N := N + 90;
>      end
>      else if  (S[i+1] = 'L') then
>      begin
>        //writeln('TryRomanToInt: Found 40');
>        Inc(i,2);
>        N := N + 40;
>      end;
>    end;
>
>    //then L
>    if (i<= Len) and (S[i] = 'L') then
>    begin
>      //writeln('TryRomanToInt: Found 50');
>      Inc(i);
>      N := N + 50;
>    end;
>
>    //then (X, xx, xxx, xxxx)
>    if (i<= Len) and (S[i] = 'X') then
>    begin
>      //find max 4 X's
>      //writeln('TryRomanToInt: Found 10');
>      Inc(i);
>      N := N + 10;
>      if (i<= Len) and (S[i] = 'X') then
>      begin
>        //writeln('TryRomanToInt: Found 10');
>        Inc(i);
>        N := N + 10;
>      end;
>      if (i<= Len) and (S[i] = 'X') then
>      begin
>        //writeln('TryRomanToInt: Found 10');
>        Inc(i);
>        N := N + 10;
>      end;
>      if (i<= Len) and (S[i] = 'X') then
>      begin
>        //writeln('TryRomanToInt: Found 10');
>        Inc(i);
>        N := N + 10;
>      end;
>    end;
>
>    //then IX or IV
>    if (i + 1<= Len) and (S[i] = 'I') then
>    begin
>      if (S[i+1] = 'X') then
>      begin
>        Terminated := (True);
>        //writeln('TryRomanToInt: Found 9');
>        Inc(i,2);
>        N := N + 9;
>      end
>      else if (S[i+1] = 'V') then
>      begin
>        Terminated := (True);
>        //writeln('TryRomanToInt: Found 4');
>        Inc(i,2);
>        N := N + 4;
>      end;
>    end;
>
>    //then V
>    if (not Terminated) and (i<= Len) and (S[i] = 'V') then
>    begin
>      //writeln('TryRomanToInt: Found 5');
>      Inc(i);
>      N := N + 5;
>    end;
>
>
>    //then I
>    if (not Terminated) and (i<= Len) and (S[i] = 'I') then
>    begin
>      Terminated := (True);
>      //writeln('TryRomanToInt: Found 1');
>      Inc(i);
>      N := N + 1;
>      //Find max 3 closing I's
>      if (i<= Len) and (S[i] = 'I') then
>      begin
>        //writeln('TryRomanToInt: Found 1');
>        Inc(i);
>        N := N + 1;
>      end;
>      if (i<= Len) and (S[i] = 'I') then
>      begin
>        //writeln('TryRomanToInt: Found 1');
>        Inc(i);
>        N := N + 1;
>      end;
>      if (i<= Len) and (S[i] = 'I') then
>      begin
>        //writeln('TryRomanToInt: Found 1');
>        Inc(i);
>        N := N + 1;
>      end;
>    end;
>
>    //writeln('TryRomanToInt: Len = ',Len,' i = ',i);
>    Result := (i>  Len);
>    //if Result then writeln('TryRomanToInt: N = ',N);
>
> end;
>
> var
>    S: String;
>    N1, N2: Integer;
>    B: Boolean;
>
> begin
>    repeat
>      write('Enter Roman numeral: ');
>      readln(S);
>      B := TryRomanToInt(S, N1);
>      if B then
>        write('TryRomanToInt(''',S,''') ->  ',N1)
>      else
>        write('TryRomanToInt(''',S,''') FAIL');
>      writeln;
>      N2 := StrUtils.RomanToInt(S);
>      writeln('StrUtils.RomanToInt(''',S,''') = ',N2);
>      if B and (N1<>  N2) then writeln('StrUtils.RomanToInt<>  TryRomanToInt');
>      writeln;
>    until S = '';
> end.
>
> Bart
> _______________________________________________
> fpc-pascal maillist  -  fpc-pascal at lists.freepascal.org
> http://lists.freepascal.org/mailman/listinfo/fpc-pascal
>




More information about the fpc-pascal mailing list