Вот мой код. Во-первых, я хочу сказать, что экспериментировал, поэтому, если вы видите здесь и там ненужные переменные, вот почему. Но основная часть моего кода находится в функции decimal моего класса romanType. Когда я ввожу определенные римские цифры, я не получаю те числа, которые мне нужны, и это может быть в моей логике где-то в моих операторах if / else.
Кстати, чтобы показать, как я двигаюсь по струне - я делаю это обратным ходом. Я иду от самого конца строки к самому началу строки, что, как мне кажется, проще с римскими цифрами. Между прочим, я также сделал тип перечисления, чтобы я мог сравнивать римские цифры, видя, какая из них меньше, какая больше и т. д. Затем я использовал карту, чтобы иметь возможность сравнивать значения перечисления со значением char.
Итак, проблема: например, когда я набираю CCC, я получаю 290 вместо 300. Если вы знаете, что не так в моей логике, я был бы очень признателен! Спасибо.
Кроме того, я новичок в программировании и был бы очень признателен за любые стилистические советы или все, что я могу узнать о классах и т. д., Которые я пропустил при написании этого кода? Пожалуйста, дайте мне знать, что лучше. Спасибо.
#include <iostream>
#include <string>
#include <map>
using namespace std;
class romanType {
string numeral;
int k;
public:
romanType();
void rnumeral (string b) {numeral = b;}
int decimal(string num, char b, int temp) {
num = "";
enum RomanNumerals {I, V, X, L, C, D, M };
map<char, RomanNumerals> m;
m['I'] = I;
m['V'] = V;
m['X'] = X;
m['L'] = L;
m['C'] = C;
m['D'] = D;
m['M'] = M;
RomanNumerals roman1;
RomanNumerals roman2;
cout << "Please type in your roman numeral:" ;
cin >> num;
for (int i =0; i <num.length()-1; i++){
}
for(long i = num.length()-1; i>=0; i--)
{
b = num[i];
if (islower(b)) b=toupper(b);
roman1 = m[num[i]];
roman2 = m[num[i-1]];
switch(b){
case 'I':
if (num[i] == num.length()-1){
temp += 1;
}
break;
case 'V':
if (roman1 > roman2){
temp += 4;
continue;
}
else {
temp += 5;
}
break;
case 'X':
if (roman1 > roman2){
temp += 9;
continue;
}
else {
temp += 10;
}
break;
case 'L' :
if (roman1 > roman2){
temp += 40;
continue;
}
else {
temp += 50;
}
break;
case 'C':
if (roman1 > roman2){
temp += 90;
continue;
}
else {
temp += 100;
}
break;
case 'D' :
if (roman1 > roman2){
temp += 400;
continue;
}
else {
temp += 500;
}
break;
case 'M':
if (roman1 > roman2){
temp += 900;
continue;
}
else {
temp += 1000;
}
break;
}
}
return temp;
}
};
romanType::romanType () {
numeral = "";
}
int main() {
string k = "";
char b = ' ';
int temp = 0;
romanType type;
type.rnumeral(k);
int c = type.decimal(k, b, temp);
cout << c;
return 0;
}
Обновлено: _____________________________________________________________________________
Я нашел решение своей проблемы. Вот мой новый код:
#include <iostream>
#include <string>
#include <map>
using namespace std;
string acceptRN();
class romanType {
string numeral;
int temp2;
int l;
// VARIABLES
public:
romanType();
//DEFAULT CONSTRUCTOR
void getRnumeral (string b)
{
numeral = b;
}
//SETTER
void decimal(string num, int temp, char b) {
num = numeral;
enum RomanNumerals {I, V, X, L, C, D, M };
map<char, RomanNumerals> m;
m['I'] = I;
m['V'] = V;
m['X'] = X;
m['L'] = L;
m['C'] = C;
m['D'] = D;
m['M'] = M;
RomanNumerals roman1;
RomanNumerals roman2;
RomanNumerals roman3;
for(long i = num.length()-1; i>=0; i--)
{
b = num[i];
if (islower(b)) b=toupper(b);
roman1 = m[num[i]];
roman2 = m[num[i-1]];
roman3 = m[num[i+1]];
switch(b){
case 'I':
if ( roman3 > roman1 && i != num.length()-1){
continue;
}
else {
temp += 1;
break;
}
case 'V':
if (roman1 > roman2 && i != 0){
temp += 4;
continue;
}
else {
temp += 5;
}
break;
case 'X':
if ( roman3 > roman1 && i != num.length()-1)
continue;
if (roman1 > roman2 && i!= 0){
temp += 9;
continue;
}
else {
temp += 10;
}
break;
case 'L' :
if (roman1 > roman2 && i!= 0){
temp += 40;
continue;
}
else {
temp += 50;
}
break;
case 'C':
if ( roman3 > roman1 && i != num.length()-1)
continue;
if (roman2 == X && i!= 0){
temp += 90;
continue;
}
else {
temp += 100;
}
break;
case 'D' :
if (roman2 == C && i!= 0){
temp += 400;
continue;
}
else {
temp += 500;
}
break;
case 'M':
if (roman2 == C && i!= 0){
temp += 900;
continue;
}
else {
temp += 1000;
}
break;
}
}
temp2 = temp;
}
void showDecimal() {
cout << "Here is your roman numeral in decimal format:";
cout << temp2 << " \n \n \n";
}
};
romanType::romanType () {
numeral = "";
}
int main() {
string k = acceptRN();
int m = 0;
char l= ' ';
romanType type;
type.getRnumeral(k);
type.decimal(k, m, l);
type.showDecimal();
return 0;
}
string acceptRN(){
string num = "";
cout << "Please type in your roman numeral:" ;
cin >> num;
return num;
}
Вы можете показать это в коде? Я не понимаю твоего ответа. @Spektre
Смотрите мой ответ. Я попытался реализовать это в вашем коде, и это то, что я придумал, похоже, что он работает, но не тестировал его всесторонне ... в моем распоряжении не так много римских чисел ...
Одна вещь, которую я заметил, это то, что вы выполняете цикл до тех пор, пока i> = 0 и не используете номер [i-1]. Итак, вы читаете за пределами массива, который является UB.





Когда я закончил свой комментарий и немного подправил ваш код, я получил следующее:
//---------------------------------------------------------------------------
int roman_ix[256] = {-1};
const int roman_val[] = { 1 , 5 ,10 ,50 ,100,500,1000,0};
const char roman_chr[] = {'I','V','X','L','C','D', 'M',0};
//---------------------------------------------------------------------------
int roman2int(char *s)
{
int i,x=0,v=0,v0;
// init table (just once)
if (roman_ix[0]<0)
{
for (i=0;i<256;i++) roman_ix[i]=0;
for (i=0;roman_chr[i];i++) roman_ix[roman_chr[i]]=i;
}
// find end of string
for (i=0;s[i];i++);
// proccess string in reverse
for (i--;i>=0;i--)
{
v0=v; // remember last digit
v=roman_val[roman_ix[s[i]]]; // new digit
if (!v) break; // stop on non supported character
if (v0>v) x-=v; else x+=v; // add or sub
}
return x;
}
//---------------------------------------------------------------------------
Я тестировал на них:
1776 1776 MDCCLXXVI
1954 1954 MCMLIV
1990 1990 MCMXC
2014 2014 MMXIV
300 300 CCC
первое число преобразуется из строки, второе - это то, что должно быть, а последнее - римская строка.
Если таблица 256 записей слишком велика, вы можете сжать ее до диапазона A-Z, который значительно меньше, но требует еще одного вычитания в коде. Его также можно жестко запрограммировать, чтобы избавиться от инициализации:
//---------------------------------------------------------------------------
int roman2int(char *s)
{
// init
int i,x=0,v=0,v0; // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
const int val['Z'-'A'+1] = { 0, 0, 100, 500, 0, 0, 0, 0, 1, 0, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 0, 0 };
// find end of string
for (i=0;s[i];i++);
// process string in reverse
for (i--;i>=0;i--)
{
if ((s[i]<'A')||(s[i]>'Z')) break; // stop on non supported character
v0=v; v=val[s[i]-'A'];
if (v0>v) x-=v; else x+=v;
}
return x;
}
//---------------------------------------------------------------------------
Поскольку я избавился от ваших temp и roman1,roman2 и условий switch if/else, и код работал с первой компиляции ... Я предполагаю, что вы делаете с ними что-то подозрительное (потерялись в комбинациях if / else, пропавших без вести какой-то крайний случай).
Я считаю вашу первую версию действительно изящной!
для начала вы можете избавиться от
switch(b), если создадите таблицу, в которой будет буквальное десятичное значение ...int value[] = {1,5,10,...}, а затем просто используйтеtemp+=value[b];, значительно упростив ваш код. Тогда вы сможете легко отлаживать свой код, так как он сильно уменьшится.