重构走过

这是一个可能受益于重构的程序。这是一个使用 C++ 11 的简单程序,用于计算和打印从 1 到 100 的所有素数,并基于在 CodeReview 上发布的程序进行审核。

#include <iostream>
#include <vector>
#include <cmath>

int main()
{
    int l = 100;
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < l; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            } else if (primes[primecount] * primes[primecount] > no) {
                std::cout << no << "\n";
                break;
            }
        }
        if (isprime) {
            std::cout << no << " ";
            primes.push_back(no);
        }
    }
    std::cout << "\n";
}

该程序的输出如下所示:

3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

我们注意到的第一件事是程序无法打印作为素数的 2。我们可以简单地添加一行代码来简单地打印一个常量,而无需修改程序的其余部分,但重构程序将其分成两部分可能更为简洁 - 一个创建素数列表另一个打印它们。这可能是这样的:

#include <iostream>
#include <vector>
#include <cmath>

std::vector<int> prime_list(int limit)
{
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < limit; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            } else if (primes[primecount] * primes[primecount] > no) {
                break;
            }
        }
        if (isprime) {
            primes.push_back(no);
        }
    }
    return primes;
}

int main() 
{
    std::vector<int> primes = prime_list(100);
    for (std::size_t i = 0; i < primes.size(); ++i) {
        std::cout << primes[i] << ' ';
    }
    std::cout << '\n';
}

尝试这个版本,我们发现它现在确实可以正常工作:

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

下一步是要注意第二个 if 子句并不是真正需要的。循环中的逻辑查找每个给定数字的素数因子,直到该数字的平方根。这是有效的,因为如果有一个数字的素数因子,其中至少有一个必须小于或等于该数字的平方根。只重做那个函数(程序的其余部分保持不变)我们得到这个结果:

std::vector<int> prime_list(int limit)
{
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < limit; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            }
        }
        if (isprime) {
            primes.push_back(no);
        }
    }
    return primes;
}

我们可以更进一步,将变量名称更改为更具描述性。例如,primecount 实际上不是素数。相反,它是已知素数向量的索引变量。此外,虽然 no 有时被用作数字的缩写,但在数学写作中,使用 n 更为常见。我们也可以通过消除 break,并通过更接近于使用它们的位置声明变量来进行一些修改。

std::vector<int> prime_list(int limit)
{
    std::vector<int> primes{2};
    for (int n = 3; n < limit; n += 2) {
        bool isprime = true;
        for (int i=0; isprime && primes[i] <= std::sqrt(n); ++i) {
            isprime &= (n % primes[i] != 0);
        }
        if (isprime) {
            primes.push_back(n);
        }
    }
    return primes;
}

我们还可以重构 main 以使用“range-for”来使它更整洁:

int main() 
{
    std::vector<int> primes = prime_list(100);
    for (auto p : primes) {
        std::cout << p << ' ';
    }
    std::cout << '\n';
}

这只是重构的一种方式。其他人可能做出不同的选择但是,重构的目的仍然是相同的,这是为了提高代码的可读性和可能性,而不必添加功能。