【问题标题】:C Program that prints Multiples of 3, 5 and both, professional style suggestion [closed]打印 3、5 和两者的倍数的 C 程序,专业风格建议 [关闭]
【发布时间】:2020-09-29 09:13:58
【问题描述】:

我编写了我的第一个简单的 C 程序,它打印 3 、 5 以及 3 和 5 的倍数而不是数字,从 1 到 100。它可以工作。

我知道这个程序可以用数百万种不同的方式编写。专业的 C 程序员将如何实施最佳实践?作为一个完美主义者,我正在努力实现最高的最佳标准,主要是指针和专业的函数实现和预处理器。

#include <stdio.h>

void findMultiples(int n);


int main() {
    
    findMultiples(100);
    
    return 0;
}

void findMultiples(int n){
    for (int i = 1; i <= n; i++)
        if (i % 15 == 0){
            printf("Multiple of 3 and 5\n");
        }
        else if (i % 5 == 0){
            printf("Multiple of 5\n");
        }
        else if (i % 3 == 0){
            printf("Multiple of 3\n");
        }
        else{
            printf("%d\n", i);
        }
}

【问题讨论】:

  • 你可以稍微修正一下缩进,通常{ 函数在行之后,for 循环多行,鼓励将它括起来{ }。另一件事,几乎是个人喜好,将具有返回值而不是 void 的函数
  • 假设代码有效,那么这个问题就是主题,应该在codereview.stackexchange.com 提问。在 SO 上是基于边界线的意见,过于宽泛。
  • (虽然这个特殊的小算法是著名的“Fizz Buzz”算法。如果你搜索“Fizz Buzz 优化”之类的,你会在网上找到很多例子。例如这个对于 CR 上的 Java:codereview.stackexchange.com/questions/74443/…)
  • 代码将对 15 个连续数字的序列进行 39 次模运算。您可以通过始终执行 i % 3 == 0i % 5 == 0 运算并存储它们的结果来将其减少到 30 个模运算。 (除法和模运算相对昂贵。)
  • void findMultiples(int n); bad name... 该函数不会在任何地方搜索任何内容(“查找”的含义)。我将其命名为printMultiples。此外,传递计算倍数的基础可能是一个好主意......并返回打印的倍数? int printMultiples(int limit, int *bases);这个新版本可以称为int p = printMultiples(100, (int[]){3, 5, 0});

标签: c modulo


【解决方案1】:

这基本上是Fizzbuzz,如果它可以被 5 和 3 整除,你应该打印 FizzBu​​zz,如果它可以被 5 整除但不能被 3 整除,你应该打印 Fizz,如果它可以被 3 但不能被 5 整除,你应该打印 Buzz。

TL;DR

没有“正确”的方法可以做到这一点。不管你怎么做,你都会破坏一些最佳实践。

详细解释

注意!

我是那种喜欢采用灵活方法来实现最佳实践和代码标准的程序员。这没有对错,但下面的整个文字都被我的个性所着色。我几乎将此测试视为“您能理解何时跳过最佳实践”,而其他人则将其视为“即使在棘手的情况下,您也能弄清楚如何遵循最佳实践”。没有一个比另一个更正确。

这个问题很容易理解,但要“很好”地做到这一点非常棘手。在此示例中您经常会遇到的一件事是重复代码。您可以很容易地构建它,但通常会以可读性为代价。

如果我要设计一个符合您要求的代码,我会对您提供的解决方案非常满意,然后继续处理下一个问题。真的不值得花很多时间在上面。这也是雇主在给您进行此测试时会考虑的事情。您是否会花费数小时和数天的时间来确保您的代码遵循所有“最佳实践”,即使收益微乎其微,还是您是否创建了有效且可读的代码,并在它足够好的时候继续前进?

我见过的一些示例可以避免首先检查它是否可以被两个数字整除,然后分别检查它们是否连接字符串。像这样的伪:

string str = ""
if n % 5 = 0: str += "Fizz"
if n % 3 = 0: str += "Buzz"
print str

看起来不错,对吧?好吧,把它翻译成真正的 C 语言,字符串处理非常混乱。

char str[9] = "";
if(n%5 == 0) strcat(str, "Fizz");
if(n%3 == 0) strcat(str, "Buzz");
puts(str);

不过看起来还不错。但这真的值得吗?如果您想将“Fizz”和“Buzz”更改为更长的名称怎么办?然后你需要确保str 有更多的空间,这很容易忘记并且可能导致难以追踪的错误。我并不是说这段代码非常危险,但这里的底线是你的推理方式。这种风险真的非常值得为避免一些代码重复而付出努力吗?

有些人将条件重构为函数,例如bool dividable_by_15(int n),因为“将功能分解为单独的函数很好”。有些人甚至会做到这一点:

bool dividable_by(int n, int d) { return (n%d) == 0; }
bool dividable_by_3(int n) { return dividable_by(n, 3); }
bool dividable_by_5(int n) { return dividable_by(n, 5); }
bool dividable_by_15(int n) { return dividable_by_3(n) && dividable_by_5(n); }

但在这种情况下真的需要它吗?我不这么认为,但我不会说选择是 100% 显而易见的,这也取决于您使用的语言。但在大多数情况下,我会说这是一个非常明显的过度工程案例。

这个测试并不是要看看您是否可以遵循所有最佳做法。这更像是一个性格测试。一些雇主希望你做各种各样的事情,而另一些雇主则希望你可以让代码在它应该做的事情时保持原样。

当谈到您的代码时,我实际上只有一个反对意见,那就是您省略了 for 循环的大括号。我永远不会那样做,除非身体是一条简单的线。但是,我会省略 if 语句的大括号。有些人会同意最后一个,有些人不会。那些即使对于单个语句也总是使用大括号的人经常使用这样的论点,即如果您需要在正文中添加额外的语句,它会降低出现错误的风险。另一个论点是一致性,即你总是应该努力在任何地方做同样的事情。在我看来,这些因素不值得多加几行,但是嘿,那就是我。你做你。

for (int i = 1; i <= n; i++) {
    if (i % 15 == 0)
         printf("Multiple of 3 and 5\n");
    else if (i % 5 == 0)
        printf("Multiple of 5\n");
    else if (i % 3 == 0)
        printf("Multiple of 3\n");
    else
        printf("%d\n", i);
}

这对我来说看起来好多了。我什至会考虑将 if 作为一个衬里:

    if      (i % 15 == 0) printf("Multiple of 3 and 5\n");
    else if (i % 5 == 0)  printf("Multiple of 5\n");
    else if (i % 3 == 0)  printf("Multiple of 3\n");
    else                  printf("%d\n", i);

我也会考虑提取换行符,如下所示:

    if      (i % 15 == 0) printf("Multiple of 3 and 5");
    else if (i % 5 == 0)  printf("Multiple of 5");
    else if (i % 3 == 0)  printf("Multiple of 3");
    else                  printf("%d", i);
    printf("\n");

由于它只是一个新行,您可以将 printf("\n") 更改为 puts("")

但是 - 事情是这样的 - 在我看来,我已经在这个问题上花费了很多精力。 :)

作为一个完美主义者,我正在努力实现最高的最佳标准

我读到的一句话完美地回答了这个问题:“盲目遵循最佳实践并不是最佳实践”

“最佳实践”旨在提供一种非常简单的方法来实现某些目标。如果您的代码在没有遵循最佳实践的情况下实现了该目的,为什么要更改它?特别是如果它更好地实现了这个目的。

【讨论】:

  • 可能取决于雇主是否优先考虑质量或上市时间。
  • @Lundin 是的,我也强调了这一点
【解决方案2】:

你的实现是最好的实现。

对于很多程序员来说,两次检查取模条件的感觉并不令人满意,并且案例的实现感觉它们非常相似,可以折叠在一起。因此,我们中的许多人会尝试以某种方式消除明显的冗余。

但是,这些技巧总是会增加代码的复杂性。事实是,这四个案例,正如你所写的,很容易理解。您可以尝试消除明显冗余的任何技巧都无法使其变得更简单。

最后,简单胜过聪明。我们知道这就是 KISS 原则:保持简单,愚蠢。优质软件不求精巧,而求简单。这使得它更易于维护、更易于扩展和更易于使用。 要优化的最重要变量是代码的简单性。最聪明的程序员会束手无策,只在需要时才释放。


也就是说,作为纯粹的风格评论,通常不建议在复杂循环或if 的主体周围省略大括号。我会将您的代码格式化为:

void findMultiples(int n) {
    for (int i = 1; i <= n; i++) {
        if (i % 15 == 0) {
            printf("Multiple of 3 and 5\n");
        }
        else if (i % 5 == 0) {
            printf("Multiple of 5\n");
        }
        else if (i % 3 == 0) {
            printf("Multiple of 3\n");
        }
        else {
            printf("%d\n", i);
        }
    }
}

否则,您总是冒着弄乱属于循环/if 和不属于循环的内容的风险。

此外,函数的命名不是最理想的。我想,我会称之为printDivisibilityList()。因为该函数不会“查找倍数”,所以它会打印所有数字并用35 的可分性对它们进行注释。

【讨论】:

  • 优点。函数名确实是我在深入研究之前应该想到的。这种风格看起来更干净。感谢您的提示
【解决方案3】:

您假设有人正在阅读输出。此外,该程序不会按照您的方式执行。它确实打印 3 和 5 的倍数,它省略它们。

一般来说,我会阅读所询问的内容,然后确定程序将如何使用,然后编写程序。 在你的情况下,这将是这样的:

#include <stdio.h>

void findMultiples(int n)
{
    for (int i = 1; i <= n; i++)
        if ((i % 3 == 0) || (i % 5 == 0)) {
            printf("%d\n", i);
        }
    }
}

int main()
{   
    findMultiples(100);

    return 0;
}

编码风格并不那么重要。

现在我为什么要从关于人类的评论开始。嗯,小程序通常被设计成链接逻辑。为什么非人类会对您打印的文本感兴趣?

我不认为这个答案是您最初的想法,但我希望它可以帮助您以稍微不同的方式思考您的程序。

【讨论】:

  • 我确实希望程序省略数字并仅打印文本。我会研究你的代码。
  • 啊,我知道我犯了一个初学者的错误。我在您编辑问题后引用了您。幸运的是,每个人都可以看到。
猜你喜欢
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 2021-02-27
  • 2013-01-20
  • 1970-01-01
相关资源
最近更新 更多