重構走過

這是一個可能受益於重構的程式。這是一個使用 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';
}

這只是重構的一種方式。其他人可能做出不同的選擇但是,重構的目的仍然是相同的,這是為了提高程式碼的可讀性和可能性,而不必新增功能。