我经常看到你可以用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)
或者我只是对"没有原始循环"的想法太过分了?
这里真正的解决方案是定义一个合适的迭代器,如:
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)
小智 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)