C # function to convert numbers

I am trying to write a practical function to convert input with roman numerals to decimal. I did this earlier than 2-way through Javascript

, but in version C#

I have a problem with iterating while in my loop and I don't know how to do it yet.

class ToRoman
{
    public static int RomanToDecimal(string romanNums)
    {
        int result = 0; 

        int[] deci = new int[] {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string[] roman = new string[] {"M", "CM", "D", "CD", "C", "XD", "L", "XL", "X", "IX", "V", "IV", "I"};

        for (int i = 0; i < deci.Length; i++)
        {
            while (romanNums.IndexOf(roman[i] == romanNums[i])
            {
                result += deci[i];
                romanNums = romanNums.Replace(roman[i]," ");
            }
        }
        return result;
    }

    static void Main()
    {
        Console.WriteLine(RomanToDecimal("V"));
        Console.WriteLine(RomanToDecimal("XIX"));
        Console.WriteLine(RomanToDecimal("MDXXVI"));
        Console.WriteLine(RomanToDecimal("MCCCXXXVII"));
    }
}

      

+3


source to share


1 answer


The very first problem with your approach is that you need to use a greedy algorithm: if you are, say, CDL

input, you should treat it as CD + L == 440

, not as C + D + L == 640

. Another (maybe minor) problem is that you are allowing the wrong type input DD

or IIV

, XM

etc.

An exact implementation with syntax checking can be written using FSA (Finate State Automata). For example. these inputs are syntactically incorrect:

   MIM, LL, XLX, CDC, XXXX, CCCXCX, CCCXL, VL, VX

      

Something like that:

private static Dictionary<char, int> s_Romans = new Dictionary<char, int>() {
  {'M', 1000}, {'D', 500}, {'C', 100}, {'L', 50}, {'X', 10}, {'V', 5}, {'I', 1},
};

private static int RomanToArabic(string value) {
  if (null == value)
    throw new ArgumentNullException("value");
  else if (string.IsNullOrWhiteSpace(value))
    throw new ArgumentException("Empty or WS only value is not allowed.", "value");

  int v;

  int[] values = value
    .Select(c => s_Romans.TryGetValue(c, out v) ? v : -1)
    .ToArray();

  int result = 0;
  int current = 1000;
  int count = 0;

  for (int i = 0; i < values.Length; ++i) {
    v = values[i];

    if (v < 0)
      throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
    else if (v > current)
      throw new FormatException($"Invalid symbol {value[i]}");
    else if (current == v) {
      count += 1;

      if (count > 1 && (v == 5 || v == 50 || v == 500))
        throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
      else if (count > 3 && current != 1000)
        throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
    }
    else {
      count = 1;
      current = v;
    }

    if (i < value.Length - 1)
      if (v == 1 || v == 10 || v == 100)
        if (v * 5 == values[i + 1] || v * 10 == values[i + 1]) {
          v = values[i + 1] - v;
          count = 3;

          i += 1;
        }

    result += v;
  }

  return result;
}

      



Some tests:

// 4000
Console.Write(RomanToArabic("MMMM")); 
// 1444
Console.Write(RomanToArabic("MCDXLIV"));
// 1009
Console.Write(RomanToArabic("MIX"));
// 1
Console.Write(RomanToArabic("I"));

      

Additional tests:

Converting whole numbers to roman numerals

// Mosè Bottacini code see the link above
public static string ToRoman(int number) {
  if ((number < 0) || (number > 3999)) 
    throw new ArgumentOutOfRangeException("insert value betwheen 1 and 3999");
  if (number < 1) return string.Empty;
  if (number >= 1000) return "M" + ToRoman(number - 1000);
  if (number >= 900) return "CM" + ToRoman(number - 900); 
  if (number >= 500) return "D" + ToRoman(number - 500);
  if (number >= 400) return "CD" + ToRoman(number - 400);
  if (number >= 100) return "C" + ToRoman(number - 100);
  if (number >= 90) return "XC" + ToRoman(number - 90);
  if (number >= 50) return "L" + ToRoman(number - 50);
  if (number >= 40) return "XL" + ToRoman(number - 40);
  if (number >= 10) return "X" + ToRoman(number - 10);
  if (number >= 9) return "IX" + ToRoman(number - 9);
  if (number >= 5) return "V" + ToRoman(number - 5);
  if (number >= 4) return "IV" + ToRoman(number - 4);
  if (number >= 1) return "I" + ToRoman(number - 1);
  throw new ArgumentOutOfRangeException("something bad happened");
}

...

var failed = Enumerable.Range(1, 3000)
    .Select(i => new {
      arabic = i,
      roman = ToRoman(i)
    })
    .Where(item => item.arabic != RomanToArabic(item.roman))
    .Select(item => $"{item.roman} expected: {item.arabic} actual: {RomanToArabic(item.roman)}");

// No line should be printed out
Console.Write(string.Join(Environment.NewLine, failed)); 

      

+1


source







All Articles