如何使用静态变量(C ++)干燥这些函数?

时间:2019-03-29 02:14:43

标签: c++ static-variables

我正在做Euler项目中的一个问题,涉及查找三角形,正方形,五角形,...,八角形数字,因此我正在尝试创建此实用程序来验证各种数字。我决定为每组数字创建筛子以进行快速访问,并将其存储在静态数组中。我能够制作一个通用的函数来生成每个筛子,但是每个验证函数都极为相似。由于它们使用静态布尔数组的方式,我看不出一种防止重复这些函数中的代码的好方法。您对此有什么想法?

#ifndef FIGURATE_NUMBERS
#define FIGURATE_NUMBERS

#define SIEVE_MAX 10000

void populateFigurateSieve(bool* sieve, const int ADDER_INCREASE)
{
    int number = 0;
    int adder = 1;

    for (int i = 0; i < SIEVE_MAX; i++)
    {
        if (i == number)
        {
            sieve[i] = true;
            number += adder;
            adder += ADDER_INCREASE;
        }
        else
        {
            sieve[i] = false;
        }
    }

    return;
}

bool isTriangleNumber(long long int n)
{
    static bool triangleNumberSieve[SIEVE_MAX];
    static bool initialized = false;

    if (!initialized)
    {
        populateFigurateSieve(triangleNumberSieve, 1);
        initialized = true;
    }

    return triangleNumberSieve[n];
}

bool isSquareNumber(long long int n)
{
    static bool squareNumberSieve[SIEVE_MAX];
    static bool initialized = false;

    if (!initialized)
    {
        populateFigurateSieve(squareNumberSieve, 2);
        initialized = true;
    }

    return squareNumberSieve[n];
}

bool isPentagonalNumber(long long int n)
{
    static bool pentagonalNumberSieve[SIEVE_MAX];
    static bool initialized = false;

    if (!initialized)
    {
        populateFigurateSieve(pentagonalNumberSieve, 3);
        initialized = true;
    }

    return pentagonalNumberSieve[n];
}

#endif

3 个答案:

答案 0 :(得分:2)

我很欣赏您的C语言方法,但是在C ++中,人们喜欢类。 (-:例如,它们允许您通过对常量值进行抽象来避免重复自己。对于三个不同的步长常数,您具有相同的代码:1、2和3,因此您可以使用如下代码为它们创建模板:< / p>

operator int

如您所见,我主要使用您的代码,但是现在它是模板类的所有静态函数。

答案 1 :(得分:1)

模板确实是一种分解代码的方法,例如:

template <std::size_t N>
constexpr std::array<bool, N> make_sieve(std::size_t ADDER_INCREASE)
{
    std::size_t number = 0;
    std::size_t adder = 1;
    std::array<bool, N> sieve{};

    for (std::size_t i = 0; i < N; i++)
    {
        if (i == number)
        {
            sieve[i] = true;
            number += adder;
            adder += ADDER_INCREASE;
        }
        else
        {
            sieve[i] = false;
        }
    }
    return sieve;
}

template <std::size_t N, std::size_t Sieve>
constexpr bool belongs(long long n)
{
    constexpr auto sieve = make_sieve<N>(Sieve);

    return sieve[n];
}

constexpr std::size_t SIEVE_MAX = 10'000;

constexpr bool isTriangleNumber(long long int n) { return belongs<SIEVE_MAX, 1>(n); }
constexpr bool isSquareNumber(long long int n) { return belongs<SIEVE_MAX, 2>(n); }
constexpr bool isPentagonalNumber(long long int n) { return belongs<SIEVE_MAX, 3>(n); }

Demo

(我更愿意使用std::bitset,但缺少一些constexpr方法:()
(如果您不能使用constexpr,则static const auto sieve = make_sieve<N>(Sieve);仅允许计算一次,而没有您的init标志)。

答案 2 :(得分:0)

void doInit(bool& initialized, bool* sieve, int adderIncrease) {
  if (!initialized) {
    populateFigurateSieve(sieve, adderIncrease);
    initialized = true;
  }
}

然后使用与调用populateFigurateSieve相同的参数来调用它,除了还要在前面传递initialized变量。

通过将初始化检查移至某个函数,而不是每次重复执行90%,可以在每个函数中节省2行。


遵循DRY原理的最佳方法是尝试查看相似代码的共同点。在这里,我注意到您正在对每个函数执行相同的初始化检查,主要区别在于您如何调用populateFigurateSieve函数。然后,我通过参数化差异来实现该功能,同时保持相同结构的相似性。

编辑:更好的是,您不需要初始化的变量。您可以让它创建并返回一个数组,而不是将指针传递给populate函数:

#include <array>
// ...
std::array<bool, SIEVE_MAX> populateFigurateSieve(const int ADDER_INCREASE) {
  std::array<bool, SIEVE_MAX> sieve {};
  // ... (Your code should still work...,)
  return sieve;
}
// ...
// When making the sieve in the function:
static std::array<bool, SIEVE_MAX> sieve = populateFigurateSieve( /* Required value here */);
// No longer need initialized variable
// ....