簡體   English   中英

如何使用靜態變量(C ++)干燥這些函數?

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

我正在從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

我很欣賞您的C方法,但是在C ++中,人們喜歡類。 (-:例如,它們允許您通過對常量值進行抽象來避免重復自己。對於三個不同的步驟常量,您具有相同的代碼: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);
}

如您所見,我主要使用您的代碼,但是現在它是模板類的所有靜態函數。

模板確實是一種分解代碼的方法,例如:

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); }

演示

(我更喜歡std::bitset ,但是缺少一些constexpr方法:()
(如果不能使用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;
  }
}

然后,使用與調用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
// ....

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM