重构走过
这是一个可能受益于重构的程序。这是一个使用 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';
}
这只是重构的一种方式。其他人可能做出不同的选择但是,重构的目的仍然是相同的,这是为了提高代码的可读性和可能性,而不必添加功能。