Пользовательская функция MySQL для перевода римского числа в арабский - PullRequest
2 голосов
/ 04 ноября 2011

Хорошо, мне нужна функция MySQL, которая преобразует строку из римских цифр:

например, XXCVI

в эквивалент арабской нумерации.Длинная история о том, зачем мне это нужно, я просто делаю.

Основываясь на PHP-функции, которую кто-то разместил, я создал следующую функцию MySQL, но, кажется, она работает бесконечно, и я не уверенЗачем.(Я мог бы просто устать)

У кого-нибудь есть намеки на то, что не так с моей функцией, или есть более эффективный способ преобразования строки римских цифр в арабское число?

DROP FUNCTION IF EXISTS `romeToArabic`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `romeToArabic`(roman_in VARCHAR(64)) RETURNS int(11)
BEGIN
  DECLARE numeral VARCHAR(2);
  DECLARE int_val INT;
  DECLARE roman VARCHAR(64);
  DECLARE res INT;
  DECLARE no_more_rows BOOLEAN;
  DECLARE num_rows INT DEFAULT 0;
  DECLARE roman_cur CURSOR FOR SELECT num, val FROM roman_numeral ORDER BY id;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
  SET roman = UPPER(roman_in);
  SET res = 0;

  DROP TEMPORARY TABLE IF EXISTS roman_numeral;
  CREATE TEMPORARY TABLE roman_numeral (
      `id` INT(8) NOT NULL AUTO_INCREMENT,
      `num` varchar(2) DEFAULT NULL,
      `val` int(8) NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM;

  INSERT INTO roman_numeral (num, val) VALUES ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1);

  OPEN roman_cur;
  SELECT FOUND_ROWS() INTO num_rows;

 the_loop:
  LOOP
    FETCH  roman_cur INTO   numeral, int_val;
    IF no_more_rows THEN CLOSE roman_cur;
      LEAVE the_loop;
    END IF;

    WHILE INSTR(roman, numeral) = 1 DO
      SET res = res + int_val;
      SET roman = SUBSTRING(roman, LENGTH(numeral));
    END WHILE;

  END LOOP the_loop;
  IF res > 0 THEN
    RETURN res;
  ELSE
    RETURN -1;
  END IF;
END$$

Ответы [ 3 ]

9 голосов
/ 04 ноября 2011

Не знаю, почему у вас не работает, но поиск в Google был быстрым, и я нашел эту ссылку:

http://forge.mysql.com/tools/tool.php?id=107

CREATE FUNCTION fromRoman (inRoman varchar(15)) RETURNS integer DETERMINISTIC
BEGIN

    DECLARE numeral CHAR(7) DEFAULT 'IVXLCDM';

    DECLARE digit TINYINT;
    DECLARE previous INT DEFAULT 0;
    DECLARE current INT;
    DECLARE sum INT DEFAULT 0;

    SET inRoman = UPPER(inRoman);

    WHILE LENGTH(inRoman) > 0 DO
        SET digit := LOCATE(RIGHT(inRoman, 1), numeral) - 1;
        SET current := POW(10, FLOOR(digit / 2)) * POW(5, MOD(digit, 2));
        SET sum := sum + POW(-1, current < previous) * current;
        SET previous := current;
        SET inRoman = LEFT(inRoman, LENGTH(inRoman) - 1);
    END WHILE;

    RETURN sum;
END
1 голос
/ 05 ноября 2011

Я изменил функцию, предоставляемую Валентином, чтобы сделать ее немного более надежной.

Теперь: 1) Обрезание пробелов перед выполнением преобразования римских цифр.2) Проверяет входящий текст на наличие любых символов, не принадлежащих римской цифре, и возвращает -1, если дело обстоит так.

    CREATE FUNCTION fromRoman (inRoman varchar(15)) RETURNS integer DETERMINISTIC
      BEGIN
DECLARE numeral CHAR(7) DEFAULT 'IVXLCDM'; DECLARE digit TINYINT; DECLARE previous INT DEFAULT 0; DECLARE current INT; DECLARE sum INT DEFAULT 0; SET inRoman = UPPER(TRIM(inRoman)); IF NOT inRoman REGEXP '[^IVXLCDM]+' THEN WHILE LENGTH(inRoman) > 0 DO SET digit := LOCATE(RIGHT(inRoman, 1), numeral) - 1; SET current := POW(10, FLOOR(digit / 2)) * POW(5, MOD(digit, 2)); SET sum := sum + POW(-1, current < previous) * current; SET previous := current; SET inRoman = LEFT(inRoman, LENGTH(inRoman) - 1); END WHILE; RETURN sum; ELSE RETURN -1; END IF; END

0 голосов
/ 05 февраля 2019

Я знаю, что на вопрос был дан полный ответ, и, возможно, это должен быть комментарий, но я пришел сюда в поисках обратного процесса, от римского к арабскому, поэтому у меня была такая функция:

CREATE FUNCTION `toRoman`(inArabic int unsigned) RETURNS varchar(15) CHARSET latin1 DETERMINISTIC
BEGIN
    DECLARE numeral CHAR(7) DEFAULT 'IVXLCDM';

    DECLARE stringInUse CHAR(3);
    DECLARE position tinyint DEFAULT 1;
    DECLARE currentDigit tinyint;

    DECLARE returnValue VARCHAR(15) DEFAULT '';

    IF(inArabic > 3999) THEN RETURN 'overflow'; END IF;
    IF(inArabic = 0) THEN RETURN 'N'; END IF;

    WHILE position <= CEIL(LOG10(inArabic + .1)) DO
        SET currentDigit := MOD(FLOOR(inArabic / POW(10, position - 1)), 10);

        SET returnValue := CONCAT(
            CASE currentDigit
                WHEN 4 THEN CONCAT(SUBSTRING(numeral, position * 2 - 1, 1), SUBSTRING(numeral, position * 2, 1))
                WHEN 9 THEN CONCAT(SUBSTRING(numeral, position * 2 - 1, 1), SUBSTRING(numeral, position * 2 + 1, 1))
                ELSE CONCAT(
                    REPEAT(SUBSTRING(numeral, position * 2, 1), currentDigit >= 5),
                    REPEAT(SUBSTRING(numeral, position * 2 - 1, 1), MOD(currentDigit, 5))
                )
            END,
            returnValue);

        SET position := position + 1;
    END WHILE;
    RETURN returnValue;
END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...