【问题标题】:How to change 4 repeated digits in roman numerals converter?如何更改罗马数字转换器中的 4 个重复数字?
【发布时间】:2016-10-26 12:52:39
【问题描述】:

我正在编写一个程序,将 0 到 3999 之间的整数转换为等效的罗马数字。

目前,我实际执行转换的主要功能工作正常。它取一个数字,用罗马数字变量组成罗马数字数组,直到用户输入的整数变为0:

int main(int argc, const char * argv[])
{
    int num;
    char roman[10];
    int I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000;

    printf("Please input a number from 1 to 3999:\n");
    scanf("%d", &num);

    do {
        for (int j = 0; j < 10; j++) {
            if (num >= M) {
                num -= M;
                roman[j] = 'M';
            }
            else if (num >= D) {
                num -= D;
                roman[j] = 'D';
            }
            else if (num >= C) {
                num -= C;
                roman[j] = 'C';
            }
            else if (num >= L) {
                num -= L;
                roman[j] = 'L';
            }
            else if (num >= X) {
                num -= X;
                roman[j] = 'X';
            }
            else if (num >= V) {
                num -= V;
                roman[j] = 'V';
            }
            else if (num >= I) {
                num -= I;
                roman[j] = 'I';
            }
        }
    } while (num != 0);

    // check(roman, 'X');

        printf("\nYour number in roman numerals is %s\n", roman);
}

注释行是我编写的一个单独的函数(称为check())。理想情况下,这个新函数应该检查任何重复 4 次的字母,如果有则返回 true,否则返回 false。它看起来像这样:

int check(char * numeralArray, char numeral)
{
    int counter = 0;
    int size = strlen(numeralArray);

    for (int i = 0; i < size; i++) {
        if(numeralArray[i] == numeral)     // check for each iteration if numeral is in numeralArray[i]
            counter++;     // if it is then counter increases by 1
        else
            counter = 0;    // this resets the value to 0 if its not consecutive.

        if(counter == 4)    // this stops the loop when 4 consecutive letters have been found.
            break;
    }

    if(counter >= 4)    // if counter is 4, it means 4 consecutive letters have been found
        return 1;   // true
    else
        return 0;   // false
}

所以我的想法是,例如,我将使用该函数并将romanI 作为参数传递给check()。然后它会检查I 是否连续出现 4 次。如果是,则该函数返回 true(由 1 表示)。在我的 main 函数中,如果从 check() 返回的值为 1(真),那么我想将这四个连续值(例如 IIII)更改为适当的值(例如 IV)。

问题是,它不能正常工作,check() 函数总是返回 0(假),即使我输入像 14 这样的数字,而不是 XIIII,它应该是 XIV .所以我不知道如何让我的函数在这种情况下返回 true,然后我必须做什么才能对数组进行所需的更改?另外,在我的 main 函数中,我是否一定需要在 do-while 循环中使用 for 循环?或者我可以摆脱 for 循环并在每次 do-while 循环运行时通过数组递增(例如j++)?

【问题讨论】:

  • 我想建议有更好的方法来组织转换,而不是生成 4 个字母然后重新映射这些字母。然而,你想要做的应该是可行的,即使不是很方便。请注意,VIIII 应该映射到 IX,但 IIII 应该映射到 IV。也就是说,找到四边形是不够的。您还必须检查它前面的字母(如果有的话;XXIIII 需要映射到 XIV;XXXXIIII 需要映射到 XLIV,等等)。
  • 检查功能确实有效,您传递的参数可能存在问题(注意大写与小写)。此外,您已经可以在转换函数中实现该功能
  • @JonathanLeffler 我同意必须有一个更简单的方法,如果你考虑到你对之前的信件所说的话,我的方法效率很低。我发现真正困难的一件事是,一旦确定了四个字母,我将如何在数组中更改它们?我对数组或指针不是很流利,所以这就是我不得不问的地方。
  • 我使用了一个包含数字和字符串的数据结构。例如,前两个条目是1000, "M"900, "CM"。我用它来指导格式化。扫描——从罗马转换为十进制——需要更多信息,但使用相同的核心数据。
  • 我想我知道你说的替换字母是什么意思。我编写了使用类似概念的程序的第二个版本,它没有这个效率那么高,但它有点工作。如果您想看,请告诉我,我会在问题中发布。

标签: c arrays return-value roman-numerals


【解决方案1】:

正确使用,您的check() 函数可以正常工作。这是一个测试它的最小main() 函数:

int main(void)
{
    char *str[] = { "IIII", "XIIII", "XIII", "IIIXIIII", "IIIXIII" };
    enum { NUM_STR = sizeof(str)/sizeof(str[0]) };

    for (int i = 0; i < NUM_STR; i++)
        printf("%s: %d\n", str[i], check(str[i], 'I'));

    return 0;
}

输出;

IIII: 1
XIIII: 1
XIII: 0
IIIXIIII: 1
IIIXIII: 0

所以,您遇到的问题不在于 check() 函数本身。


这是格式化罗马数字的另一种方法。它使用表格来确定什么是可接受的(并将数字范围限制为 1 .. 3999)。在我的主库中,该结构有两个额外的字段来帮助解析和验证罗马数字字符串,将它们转换为 int

#include <ctype.h>
#include <errno.h>

/* Header "roman.h" */
extern int fmt_roman(int i, char *buffer, size_t buflen);
/* End "roman.h" */

typedef struct Roman
{
    int           value;
    const char    chars[3];
} Roman;

enum { MAX_PERMITTED_ROMAN = 4000 };

static const Roman numerals[] =
{
    {   1000,   "M",    },
    {    900,   "CM",   },
    {    500,   "D",    },
    {    400,   "CD",   },
    {    100,   "C",    },
    {     90,   "XC",   },
    {     50,   "L",    },
    {     40,   "XL",   },
    {     10,   "X",    },
    {      9,   "IX",   },
    {      5,   "V",    },
    {      4,   "IV",   },
    {      1,   "I",    },
};
enum { NUM_NUMERALS = sizeof(numerals) / sizeof(numerals[0]) };

int fmt_roman(int i, char *buffer, size_t buflen)
{
    int rc = -1;

    if (i <= 0 || i > MAX_PERMITTED_ROMAN)
        errno = EDOM;
    else if (buflen <= 1)
        errno = ENOSPC;
    else
    {
        char *end = buffer + buflen;
        char *dst = buffer;
        const Roman *e = numerals + NUM_NUMERALS;

        for (const Roman *r = numerals; i > 0 && r < e; r++)
        {
            while (i >= r->value)
            {
                const char *src = r->chars;
                char c;
                while ((c = *src++) != '\0' && dst < end)
                    *dst++ = c;
                if (dst >= end)
                    break;
                i -= r->value;
            }
        }
        if (dst < end)
        {
            *dst = '\0';
            rc = 0;
        }
        else
            errno = ENOSPC;
    }
    return(rc);
}

和往常一样,很多代码都与错误处理相关。

【讨论】:

  • 正如 Zach P 所说,它与论点有关吗?这有点令人困惑,因为我不是一个经验丰富的程序员,可以这么说。另外,enum 是什么意思?我以前见过它,但我不太清楚它是什么?
  • 可能,但由于我们看不到您对该函数的非工作调用,因此很难知道。然而,问题不在于函数——这意味着它可能出在调用函数的代码中。
  • 我还会注意到check() 的接口并不是特别有用。您需要知道序列从哪里开始,以便准确映射它。您永远不会看到重复的 V、L、D(“五”)。
  • 更新:check() 函数确实有效,我在终端中尝试过。但它在 Xcode 中不起作用,所以我只需要从现在开始手动编译它。但是好的,这是有道理的,这个函数可以工作,所以现在只是在有四元组的地方对数组进行排序的问题。
  • 不能在 XCode 中工作,但从命令行工作几乎没有意义。
猜你喜欢
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多