- tbx::roman_numeral_to_int
- tbx::integer_to_roman_numeral
- tbx::is_roman_numeral
- tbx::roman_numeral_to_int возвращает 0 в случае сбоя.
- tbx::integer_to_roman_numeral возвращает "" в случае сбоя.
Как можно закодировать функцию tbx::roman_numeral_to_int так, чтобы она одновременно преобразовывала допустимые римские цифры и обнаруживала недопустимые? Акцент делается на обнаружении неудачных преобразований. Я не могу найти сообщений, объясняющих, как это сделать.
Можно подумать, что этот вопрос задавался и на него много раз отвечали на Stack Overflow, но на самом деле это не так. Все ответы, описывающие, как преобразовать римские цифры в целые числа, полным провалом при столкновении с недопустимыми строками римских цифр.
Это потому, что они используют алгоритмы, которые просто сканируют поперек. строку, а также складывать или вычитать значения различных символов римских цифр, которые она содержит. Никаких попыток правильно проанализировать римскую цифру не предпринимается.
Режим тестирования
Файл tbx.use_tests.RomanNumeral.cpp (источник код ниже) содержит две функции, проверяющие процедуры преобразования.- test_valid_Roman_numerals – Проверьте каждую допустимую римскую цифру! Преобразуйте каждое целое число от 1 до 3999 в римскую цифру и обратно. Подсчитайте (и сообщите) количество сбоев.
- test_invalid_Roman_numerals – попытка преобразовать множество недопустимых римских цифр. Сообщайте об успехе или неудаче для каждого из них.
Вот результат, который я получил с помощью превосходной функции преобразования @gd1.
Round-trip Conversion of Every Valid Roman Numeral
Failure count: 0
Detect Invalid Roman Numerals
Roman
Numeral Integer Valid?
"" 0 false
"garbage" 0 false
"I I" 0 false
"IL" 49 true
"IC" 99 true
"ID" 499 true
"IM" 999 true
"VX" 5 true
"VL" 45 true
"VC" 95 true
"VD" 495 true
"VM" 995 true
"XD" 490 true
"XM" 990 true
"LC" 50 true
"LD" 450 true
"LM" 950 true
"DM" 500 true
"IVIV" 8 true
"IXIX" 18 true
"IXIV" 13 true
"IVIX" 3 true
"XCXL" 130 true
"CDCM" 300 true
"VV" 10 true
"LL" 100 true
"DD" 1000 true
"IIII" 4 true
"XXXX" 40 true
"CCCC" 400 true
"MMMM" 4000 true
"IXX" 19 true
"XCC" 190 true
"CMM" 1900 true
"IIV" 3 true
"IIX" 8 true
"XXL" 30 true
"XXC" 80 true
"CCD" 300 true
"CCM" 800 true
"VIX" 4 true
"LXC" 40 true
"DCM" 400 true
IVIX равен 3? DCM равен 400?
Как-то смешно!
Исходный код
следующие разделы содержат исходный код для файлов, упомянутых выше. Он скомпилирован для C++17.Исходный код файла tbx.RomanNumeral.cpp указан в ответах. Все остальные файлы перечислены ниже.
Комментарии в tbx.use_tests.RomanNumeral.cpp объясняют, что не так с каждой из недопустимых проверяемых римских цифр.
main.cpp
// main.cpp#include
#include "tbx.use_tests.RomanNumeral.h"
int main()
{
auto& log{ std::cout };
return tbx::use_tests_RomanNumeral(log) ? 0 : 1;
}
// end file::main.cpp
tbx.RomanNumeral.h
#pragma once// tbx.RomanNumeral.h
#include
#include
#include
#include "tbx.utility.h"
namespace tbx
{
//==================================================================
// integer_to_roman_numeral
//==================================================================
template
< typename IntType
, typename = typename std::enable_if_t
>
std::string integer_to_roman_numeral(IntType const n)
{
if (n < 1 || n > 3999)
return ""; // sentinel value signals failure
static constexpr char const* const I[]{ "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
static constexpr char const* const X[]{ "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
static constexpr char const* const C[]{ "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
static constexpr char const* const M[]{ "", "M", "MM", "MMM" };
auto const v{ static_cast(n) };
enum : std::size_t { fifteen = 15u };
std::string r;
r.reserve(fifteen); // room enough for "MMMDCCCLXXXVIII", the widest Roman numeral
r = M[v / 1000u];
r += C[(v % 1000u) / 100u];
r += X[(v % 100u) / 10u];
r += I[v % 10u];;
return r;
}
//==================================================================
// is_roman_numeral
//==================================================================
bool is_roman_numeral(std::string const& r);
//==================================================================
// roman_numeral_to_int
//==================================================================
int roman_numeral_to_int(std::string r);
}
// end file: tbx.RomanNumeral.h
tbx.use_tests.RomanNumeral.h
#pragma once// tbx.use_tests.RomanNumeral.h
#include
namespace tbx
{
bool use_tests_RomanNumeral(std::ostream& log);
}
// end file: tbx.use_tests.RomanNumeral.h
tbx.use_tests.RomanNumeral.cpp
// tbx.use_tests.RomanNumeral.cpp#include
#include
#include
#include "tbx.RomanNumeral.h"
#include "tbx.utility.h"
namespace
{
bool test_valid_Roman_numerals(std::ostream& log)
{
log
Подробнее здесь: https://stackoverflow.com/questions/782 ... an-numeral