在没有raw循环的情况下向stl容器添加数据

Sta*_*rey 8 c++ stl c++11

我经常看到你可以用stl算法替换所有手写/原始循环.为了提高我的C++知识,我一直在努力.

要使用数据填充std :: vector,我使用for循环和循环索引.

unsigned int buffer_size = (format.getBytesPerSecond() * playlen) / 1000;

    // pcm data stored in a 'short type' vector
    vector<short> pcm_data;

    for (unsigned int i = 0; i < buffer_size; ++i)
    {
        pcm_data.push_back( static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)) );
    }
Run Code Online (Sandbox Code Playgroud)

上面的代码工作正常,你可以看到我使用for循环索引'i'使算法正确.

怎么有人用标准的东西取代那个for循环呢?

我见过的几乎只允许我这样做的函数是std :: transform和std :: generate,但是这两个函数都不起作用,因为我需要一个索引值来增加代码.

例如:

generate_n(begin(pcm_data), buffer_size, [] ()
    {
        return static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)); //what is i??
    });

    transform(begin(pcm_data), end(pcm_data), begin(pcm_data) [] (???)
    {
        return static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)); //what is i??
    });
Run Code Online (Sandbox Code Playgroud)

或者我只是对"没有原始循环"的想法太过分了?

Jam*_*nze 7

这里真正的解决方案是定义一个合适的迭代器,如:

class PcmIter : public std::iterator<std::forward_iterator_tag, short>
{
    int myIndex;
    double myAmplitude;
    double myFrequency;
    short myValue;

    void calculate()
    {
        myValue = myAmplitude * std::sin( 2 * M_PI * myIndex * frequency );
    }
public:
    PcmIter( int index, amplitude = 0.0, frequency = 0.0 )
        : myIndex( index )
        , myAmplitude( amplitude )
        , myFrequency( frequency )
    {
        calculate();
    }

    bool operator==( PcmIter const& other ) const
    {
        return myIndex == other.myIndex;
    }
    bool operator!=( PcmIter const& other ) const
    {
        return myIndex != other.myIndex;
    }
    const short& operator*() const
    {
        return myValue;
    }

    PcmIter& operator++()
    {
        ++ myIndex;
        calculate();
    }

    PcmIter operator++( int )
    {
        PcmIter results( *this );
        operator++();
        return results;
    }
};
Run Code Online (Sandbox Code Playgroud)

在实践中,我怀疑你可以通过 operator*返回一个值,你在那时计算,而没有一个myValue成员.

使用:

std::vector<short> pcmData(
    PcmIter( 0, amplitude, frequency),
    PcmIter( buffer_size ) );
Run Code Online (Sandbox Code Playgroud)

(幅度和频率与最终迭代器无关,因为它永远不会被解除引用.)

理想情况下,这将是一个random_access_iterator,因此vector的构造函数将计算元素的数量,并预先分配它们.然而,这涉及实现更多功能.

如果你是勇敢的,并且必须做很多类似的事情,你可以考虑让迭代器成为一个模板,在你感兴趣的函数上进行实例化.

虽然我最近没有机会和他们一起玩,但如果你正在使用Boost,你可能会考虑将a transform_iterator和a 链接起来counting_iterator.它仍然有点罗嗦,但是在Boost中完成迭代器的人们尽可能地做到了最好,因为STL迭代器的设计有点破碎.


小智 5

您可以简单地使用"generate_n"范围内的变量来声明您的变量.

unsigned int i = 0;
generate_n(begin(pcm_data), buffer_size, [&] ()
    {
        return static_cast<short>(amplitude * sin((2 * M_PI * (i++) * frequency) / format.SampleRate)); //what is i??
    });
Run Code Online (Sandbox Code Playgroud)

  • 如果他按原样使用它,他将必须用正确的条目数初始化`pcm_data`.他可以通过传递`std :: back_inserter(pcm_data)`作为第一个参数来避免这种要求,但是创建具有正确元素数量的向量可能是更好的解决方案. (2认同)

小智 5

我推荐counting_iterator在Boost Library中.一对计数迭代器为您提供一个整数范围.显然,没有底层容器.它提供整数"懒惰".该库提供了make_counting_iterator用于创建它的工厂功能.

back_insert_iteratorback_inserter标准库(标题库)中的(带工厂函数)iterator有效地调用push_back容器的成员.

使用这些成分,您可以使用transform"索引".

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

#include <boost/iterator/counting_iterator.hpp>

int main(int argc, char* argv[])
{
    // Create a pair of counting iterators
    auto first = boost::make_counting_iterator(0);
    auto last = boost::make_counting_iterator(10);

    vector<int> vi;

    // Construct a vector of a few even number, as an example.
    transform(first, last, back_inserter(vi), [](int i){ return 2 * i; });

    // Print the result for check
    copy(vi.begin(), vi.end(), ostream_iterator<int>{cout, "  "});

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

打印出来:

0  2  4  6  8  10  12  14  16  18
Run Code Online (Sandbox Code Playgroud)