C ++: Как заменить сложную итерацию одной командой? - PullRequest
0 голосов
/ 09 октября 2018

Вопрос

У меня иногда бывает сложный итерационный процесс, который должен повторяться в коде несколько раз, но выражение, которое выполняется на каждой итерации, отличается в разных местах кода.Необходимость переписывать итерационный процесс везде уродлива и подвержена ошибкам.Как я могу просто обернуть этот процесс итерации?

Пример

Рассмотрим, например, эту относительно сложную итерацию

std::string bitmask(K, 1); // K leading 1's
bitmask.resize(N, 0); // N-K trailing 0's

// print integers and permute bitmask
do {
    // Loop through BIG and SMALL indices
    for (size_t BIGindex = 0; BIGindex < nbBigs; ++BIGindex)
    {
        size_t nbSmalls;
        if (BIGindex == nbBigs)
        {
            nbSmalls = nbSmallsOfLastBig;
        } else
        {
            nbSmalls = nbSmallsStandard;
        }
        for (size_t SMALLindex = 0; SMALLindex < nbSmalls; ++SMALLindex)
        {
            // doStuff with bitmask, BIGindex and SMALLindex
        }        
    }
} while (std::prev_permutation(bitmask.begin(), bitmask.end()));

Как определить команду/ псевдоним (из-за отсутствия лучшего слова), например, doComplexItered, который превращает все эти итерации в более простую команду.Что-то вроде

doComplexIteration
{
    // doStuff with bitmask, BIGindex and SMALLindex       
}

Одно не вполне удовлетворительное решение

Один из способов сделать это - обернуть вещи, которые нужно выполнить, в такую ​​функцию, как

void doComplexIterationOnFunction(void (*doStuff)(std::string bitmask, size_t BIGindex, size_t SMALLindex))
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's

    // print integers and permute bitmask
    do {
        // Loop through BIG and SMALL indices
        for (size_t BIGindex = 0; BIGindex < nbBigs; ++BIGindex)
        {
            size_t nbSmalls;
            if (BIGindex == nbBigs)
            {
                nbSmalls = nbSmallsOfLastBig;
            } else
            {
                nbSmalls = nbSmallsStandard;
            }
            for (size_t SMALLindex = 0; SMALLindex < nbSmalls; ++SMALLindex)
            {
                (*doStuff)(bitmask, BIGindex, SMALLindex);
            }        
        }
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}

, а затем вызвать его как

doComplexIterationOnFunction(doSpecificStuff);

, однако, это заставляет меня систематически переносить любой код, который я хотел бы выполнять на каждой итерации, в функцию, которая немного громоздка инемного глупо, так как иногда код очень короткий.

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Существует другая опция:

Инвертировать управление, записывая диапазон или, по крайней мере, достаточно от одного до использовать диапазон для :

struct ComplexIterationRange {
    static constexpr auto end() noexcept { struct {} r; return r; }
    static auto begin() {
        struct {
            std::string bitmask;
            std::size_t SMALLindex = 0, BIGindex = 0;
            const auto& operator*() const noexcept { return *this; }
            auto& operator++() noexcept {
                if (++SMALLindex >= nbSmallsStandard) {
                    if (++BIGindex >= nbBigs) {
                        if (!std::prev_permutation(bitmask.begin(), bitmask.end()))
                            return *this;
                        BIGindex = 0;
                    }
                    SMALLindex = 0;
                }
                return *this;
            }
            bool operator!=(decltype(end())) const noexcept {
                return SMALLindex < nbSmallsStandard || BIGindex < nbBigs;
            }
        } r { []{ std::string r(K, 1); r.resize(N, 0); return r; }() };
        return r;
    }
};

Используйте это как:

for (auto&& x : ComplexIterationRange()) {
    Use x.SMALLindex, x.BIGindex, and x.bitmask here
    ...
}

Преимущество перед передачей функции-указателя или даже лямбда-функции-шаблона заключается в большей гибкости и контроле для вызывающей стороны.
Стоимость связывает ваш мозг в узлыво время записи диапазона.

0 голосов
/ 09 октября 2018

Вместо того, чтобы брать указатель на функцию, вы можете просто сделать функцию типом шаблона, и тогда это позволит вам передавать лямбду на сайт вызова.Это было бы похоже на

temaplte<typename Function>
void doComplexIterationOnFunction(Function doStuff)
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's

    // print integers and permute bitmask
    do {
        // Loop through BIG and SMALL indices
        for (size_t BIGindex = 0; BIGindex < nbBigs; ++BIGindex)
        {
            size_t nbSmalls;
            if (BIGindex == nbBigs)
            {
                nbSmalls = nbSmallsOfLastBig;
            } else
            {
                nbSmalls = nbSmallsStandard;
            }
            for (size_t SMALLindex = 0; SMALLindex < nbSmalls; ++SMALLindex)
            {
                std::invoke(doStuff, bitmask, BIGindex, SMALLindex);
            }        
        }
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}

Тогда вы бы назвали это как

doComplexIterationOnFunction(doSpecificStuffFunction) // pass function
doComplexIterationOnFunction(doSpecificStuffFuntor) // pass functor
doComplexIterationOnFunction([](auto foo, auto bar, auto baz) { return foo + bar - baz; }) // pass lambda
...