简体   繁体   English

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

[英]How can I DRY up these functions with static variables (C++)?

I'm doing a problem from Project Euler that involves finding Triangle, Square, Pentagonal, ..., Octagonal numbers, so I'm trying to create this utility that will validate each kind of number. 我正在从Euler项目中遇到一个问题,该问题涉及查找三角形,正方形,五边形,...,八边形的数字,因此我正在尝试创建此实用程序来验证每种数字。 I decided to create sieves for each set of numbers for fast accessing, and I'm storing it in a static array. 我决定为每组数字创建筛子以进行快速访问,并将其存储在静态数组中。 I was able to make a generic function that will generate each sieve, but that leaves each of the validation functions extremely similar. 我能够制作一个通用的函数来生成每个筛子,但是每个验证函数都极为相似。 I don't see a good way to keep from repeating the code in these functions because of the way they use the static bool arrays. 由于它们使用静态布尔数组的方式,我看不出一种防止重复这些函数中的代码的好方法。 What are some ideas you have for DRYing this up? 您有什么干这个的想法?

#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

I admire your C approach, but here in C++ people like classes. 我很欣赏您的C方法,但是在C ++中,人们喜欢类。 (-: For example, they allow you to Not Repeat Yourself by abstracting over constant values. You have the same code for three different step constants: 1, 2 and 3, so you can create a template for them using something like this: (-:例如,它们允许您通过对常量值进行抽象来避免重复自己。对于三个不同的步骤常量,您具有相同的代码:1、2和3,因此您可以使用类似的方法为它们创建模板:

#include <vector>

constexpr long long SIEVE_MAX = 10000;

template <int ADDER_INCREASE>
class GenericSieve
{
    static std::vector<bool> Sieve;

    static std::vector<bool> populated_sieve()
    {
        int number = 0;
        int adder = 1;
        std::vector<bool> sieve(SIEVE_MAX);

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

        return sieve;
    }
public:
    static bool belongs(long long n)
    {
        if (Sieve.size() == 0)
        {
            Sieve = populated_sieve();
        }
        return Sieve.at(n);
    }
};
template<int inc>
std::vector<bool> GenericSieve<inc>::Sieve;

// define a sieve for every number you like
using TriangularSieve = GenericSieve<1>;
using SquareSieve = GenericSieve<2>;
using PentagonalSieve = GenericSieve<3>;

// define functions if you will
bool isTriangleNumber(long long int n)
{
    return TriangularSieve::belongs(n);
}
bool isSquareNumber(long long int n)
{
    return SquareSieve::belongs(n);
}
bool isPentagonalNumber(long long int n)
{
    return PentagonalSieve::belongs(n);
}

As you can see, I mostly used your code, but now it's all static functions of templated classes. 如您所见,我主要使用您的代码,但是现在它是模板类的所有静态函数。

Template is indeed a way to factorize code, for example: 模板确实是一种分解代码的方法,例如:

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 演示

(I would have preferred std::bitset , but missing some constexpr methods :( ) (我更喜欢std::bitset ,但是缺少一些constexpr方法:()
(If you cannot use constexpr , static const auto sieve = make_sieve<N>(Sieve); would allow to compute it only once, without your init flag). (如果不能使用constexpr ,则static const auto sieve = make_sieve<N>(Sieve);将只计算一次,而无需使用init标志)。

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

You then call it with the same parameters as you were calling populateFigurateSieve before except you also pass the initialized variable at the front. 然后,使用与调用populateFigurateSieve之前相同的参数来调用它,除了还要在前面传递initialized变量。

That saves 2 lines in each function by moving the initialization check to a function rather than repeating 90% of it each time. 通过将初始化检查移至一个函数,而不是每次重复执行90%,可以在每个函数中节省2行。


The best way to follow the DRY principle is by trying to see what similar code have in common. 遵循DRY原理的最佳方法是尝试查看相似代码的共同点。 Here, I noticed you were performing the same initialization check for each function with the primary difference being how you called the populateFigurateSieve function. 在这里,我注意到您正在对每个函数执行相同的初始化检查,主要区别在于您如何调用populateFigurateSieve函数。 I then made the function by parameterizing the differences while keeping the same general structure from the similarities. 然后,我通过对差异进行参数化来制作此函数,同时保持相同结构的相似性。

Edit: Better yet, you don't need the initialized variable. 编辑:更好的是,您不需要初始化的变量。 Instead of passing a pointer to the populate function, you can have it create and return an array: 您可以让它创建并返回一个数组,而不是将指针传递给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
// ....

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM