代码之家  ›  专栏  ›  技术社区  ›  Remi.b

C++:如何用单个命令替换复杂的迭代?

  •  2
  • Remi.b  · 技术社区  · 7 年前

    问题

    我有时有复杂的迭代过程,必须在代码中重复多次,但每次迭代执行的表达式在代码的不同位置不同。必须到处重写迭代过程是丑陋的,而且容易出错。如何包装这个迭代过程?

    例如,考虑这个相对复杂的迭代

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

    我如何定义一个命令/别名(因为没有更好的词),比如“doComplexIteration”,它可以将所有这些迭代打包成一个更简单的命令。像这样的

    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 回复  |  直到 7 年前
        1
  •  3
  •   NathanOliver    7 年前

    不用使用函数指针,只需将函数设置为模板类型,然后就可以在调用站点传递lambda。那看起来像

    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
    
        2
  •  3
  •   Deduplicator    7 年前

    有一个不同的选择:

    反转控件,方法是写入一个范围,或至少写入足够的范围 use range-for :

    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
        ...
    }
    

    与传递函数指针,甚至lambda到模板函数相比,它的优点是调用方具有更大的灵活性和控制能力。
    成本是把你的大脑绑成绳结,而写的范围。

    推荐文章