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

时间:2014-10-14 10:45:16

标签: c++ c++11 stl

我经常看到你可以用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)) );
    }

上面的代码工作正常,你可以看到我使用for循环索引&#39; i&#39;为了使算法正确。

有人如何用标准中的内容替换for循环?

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

EG:

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

或者我只是过分了解&#34;没有原始循环&#34;

5 个答案:

答案 0 :(得分: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;
    }
};

在实践中,我怀疑你可以顺其自然 operator*返回一个您在此时计算的值, 并且没有myValue成员。

使用:

std::vector<short> pcmData(
    PcmIter( 0, amplitude, frequency),
    PcmIter( buffer_size ) );

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

理想情况下,这将是一个random_access_iterator,以便 向量的构造函数将计算元素的数量,和 预先分配它们。这涉及实施更多 但是,功能。

如果你勇敢,并且必须做很多类似的事情,那么你 可以考虑将迭代器作为模板 实例化你感兴趣的功能。

虽然我最近没有机会和他们一起玩,但是 你正在使用Boost,你可能会考虑链接 一个transform_iterator和一个counting_iterator。它仍然是 有点罗嗦,但在Boost做过迭代器的人做了 鉴于STL的设计有些破碎,他们能做到最好 迭代器。

答案 1 :(得分: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??
    });

答案 2 :(得分:5)

我会推荐counting_iterator in Boost Library。一对计数迭代器为您提供一个整数范围。显然,没有底层容器。它提供整数&#34;懒惰&#34;。该库提供了工厂函数make_counting_iterator来创建它。

标准库(标题back_insert_iterator)中的

back_inserter(带有工厂函数iterator)有效地调用容器的成员push_back

使用这些成分,您可以将transform与&#34;索引&#34;一起使用。

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

打印输出:

0  2  4  6  8  10  12  14  16  18

答案 3 :(得分:1)

不一定更好,但是使用stl:

的解决方案
struct generate_value {

short operator() () const {return amplitude * sin((2 * M_PI * i++ * frequency) / format.SampleRate);}

private:

unsigned i = 0;
};

generate_n(back_inserter(pcm_data), buffer_size, generate_value{});  

答案 4 :(得分:0)

我看到了一些我尚未见过的可能性。人们可以从一系列数字的迭代器开始:

template <class T>
class xrange_t {
    T start;
    T stop;
public:
    xrange_t(T start, T stop) : start(start), stop(stop) {}

    class iterator : public std::iterator<std::forward_iterator_tag, T> {
        T current;
    public:
        iterator(T t) : current(t) {}
        T operator *() { return current; }
        iterator &operator++() { ++current; return *this; }
        bool operator!=(iterator const &other) const { return current != other.current; }
        bool operator==(iterator const &other) const { return current == other.current; }
    };

    iterator begin() { return iterator(start); }
    iterator end() { return iterator(stop); }
};

template <class T>
xrange_t<T> xrange(T start, T stop) {
    return xrange_t<T>(start, stop);
}

然后你将它用于一个ranged-for循环来完成真正的工作:

#include "xrange"

for (auto i : xrange(0, buffer_size))
    pcm_data.push_back( static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)) );

另一种可能性是通过几个步骤完成工作:

std::vector<short> pcm_data(buffer_size);

std::iota(pcm_data.begin(), pcm_data.end(), 0);
std::transform(pcm_data.begin(), pcm_data.end(), pcm_data.begin(), 
    [](short i) { 
        return static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)));
    }
);

首先用连续的i值(即函数的输入)填充数组,然后将每个输入转换为匹配的输出值。

这有两个潜在的缺点:

  1. 如果i的值可能超过short中可以存储的值,则可能会在初始存储阶段截断输入值。不清楚您对int使用i是否反映了它可能具有更大幅度的可能性,或者默认情况下仅使用int
  2. 它遍历结果向量两次。如果向量很大(特别是如果它太大而无法容纳在缓存中),这可能会慢得多。