[fpc-pascal] Re: StrUtils.RomanToInt oddities

Lukasz Sokol el.es.cr at gmail.com
Tue Sep 24 12:48:37 CEST 2013


On 23/09/13 17:18, Bart wrote:
> On 9/23/13, Lukasz Sokol <el.es.cr at gmail.com> wrote:
> 
>> function TryRomanToInt(AInput:String; out AResult: integer):boolean;
>> var i, Len, N, Np1 : integer;
> [snip]
>>
>>
>>         if N >= Np1 then AResult := AResult + N
>>         else AResult := AResult - N;
>>
>>       end;
>>     else // i = Len-1 = last char we just add (consider : MCM : add 1000,
> 
> Compiler doesn't like the else here. I removed the ; after the end 1 line above.
I said below it's only mind-compiled ;)

> 
>> sub 100, add 1000 = 1900)
>>      begin
>>        AResult := AResult + N;
>>      end;
>>   end; // for
>>   // if the above, after all characters are through, has resulted in 0 or
>> less,
>>   // we invalidate everything at the end (consider : CMLM, IIIM )
>>
>>   Result := AResult > 0;
>>   if not Result then AResult := 0;
>> end;
>>
>> (only mind-compiled ;) tests welcome ;) )
>>
> 
> It produces "odd" output though:
> 
> C:\Users\Bart\LazarusProjecten\ConsoleProjecten>test
> Enter Roman numeral: LL  //should be illegal
> TryRomanToInt('LL') -> 50
> StrUtils.RomanToInt('LL') = 100
Oh I see the loop needs to run from 0 to Len-1 ?.... it missed the last (or first) char

> 
> Enter Roman numeral: MM
> TryRomanToInt('MM') -> 1050  //obviously should be 2000
But this looks like you're adding the results? Or are you reusing the
same variable ? In that case stick AResult := 0 at the top of the function please

> StrUtils.RomanToInt('MM') = 2000
> 
> Enter Roman numeral: II
> TryRomanToInt('II') -> 1051  // nice one!  
Yeah totally added to former Aresult, and missed last char ...

> StrUtils.RomanToInt('II') = 2
> 
> So, it's a little bit flawed.

:)

function TryRomanToInt(AInput:String; out AResult: integer):boolean;
var i, Len, N, Np1 : integer;

  function TryRomanCharToInt(AIn: Char; out AOut: integer): boolean;
  begin
    case AIn of
      'M' : AOut := 1000;
      'D' : AOut := 500;
      'C' : AOut := 100;
      'L' : AOut := 50;
      'X' : AOut := 10;
      'V' : AOut := 5;
      'I' : AOut := 1;
    else
      AOut := 0;
    end;
    Result := (AOut > 0);
  end;

begin
  // if it looks like shameless c&p, it's because it is ;)
  Result := False;
  AResult := 0; // << here you've reused the variable passed to AResult somewhere
  AInput := UpperCase(AInput);  //don't use AnsiUpperCase please
  Len := Length(AInput);
  if (Len = 0) then Exit;
  // 
  i := 1;
  for i := 0 to Len-1 do // which char am I actually missing here??? 
  begin
    if not TryRomanCharToInt(AInput[i], N) then
    begin
      AResult := -1; // invalidate everything
      exit;
    // writeln('Not a valid roman numeral at position ',i);
    end;

    if (i > Len-1) then 
      begin 
        if not TryRomanCharToInt(AInput[i+1], Np1) then
        begin
          AResult := -1; // invalidate everithing
          exit;
        // writeln('Not a valid roman numeral at position ',i+1);
        end;
         
	// according to wikipedia, .
        if not (((N = 100) and (Np1 > 100)) or  // C can be placed before L or M
                ((N = 10) and (Np1 > 10) and (Np1 <= 100)) or // X can be placed before L or C
                ((N = 1) and (Np1 > 1) and (Np1 <= 50)))  // I can be placed before V and X << here too
        then
          begin
            AResult := -1; // invalidate everithing: catches MDCLXVIVXLDM
            exit;
            // writeln('Not a valid roman numeral combination at position ',i, ' and ',i+1);
          end;
  

        if N >= Np1 then AResult := AResult + N
        else AResult := AResult - N;
        
      end // i > Len-1, you're right about the ; 
    else // i = Len-1 = last char we just add (consider : MCM : add 1000, sub 100, add 1000 = 1900)
     begin // alternatively this should exit the loop maybe on the N-1th character 
       AResult := AResult + N;
     end;
  end; // for
  // if the above, after all characters are through, has resulted in 0 or less,
  // we invalidate everything at the end (consider : CMLM, IIIM )
  
  Result := AResult > 0;
  if not Result then AResult := 0;
end;


> 
> Anyhow, let's not focus upon what the new code should be.
> The question was: is current behaviour (accepting IIMIIC etc.) a bug or not.
> 
> 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