C#函数转换数字

C# function to convert Numerals

我正在尝试编写一个实用的函数来将罗马数字输入转换为十进制。我之前已经通过 Javascript 在两个方向上做到了,但是在 C# 版本中我的循环中的 while 迭代有问题,我还不知道如何让它工作。

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"));
    }
}

你的方法的第一个问题是你必须使用贪心算法:如果你有,说CDL 输入您应该将其视为 CD + L == 450,而不是 C + D + L == 650。另一个(可能是次要的)问题是您允许不正确的输入,例如 DDIIVXM

可以在 FSA[=43= 的帮助下编写具有 语法检查 准确 实现](有限状态自动机)。例如。这些输入在语法上不正确:

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

像这样:

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;
}

一些测试:

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

更多测试:

Converting integers to roman numerals

// Mosè Bottacini's code see the link above
public static string ToRoman(int number) {
  if ((number < 0) || (number > 3999)) 
    throw new ArgumentOutOfRangeException("insert value between 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));