将数据添加到没有原始循环的 STL 容器
adding data to stl container without raw loops
我经常看到你可以用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 循环索引"i"表示算法正确。
有人怎么能用标准中的东西替换它?
我见过的唯一允许我这样做的函数是 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??
});
还是我只是太过分地了解"没有原始循环"的想法?
这里真正的解决方案是定义一个适当的迭代器,如下所示:
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 的设计有些破碎,他们尽其所能迭代器。
您可以简单地在"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??
});
我建议在Boost Library中counting_iterator
。一对计数迭代器为您提供一个整数范围。显然,没有底层容器。它"懒惰地"提供整数。该库提供用于创建它的工厂函数make_counting_iterator
。
标准库(标头 iterator
(中的back_insert_iterator
(使用工厂函数 back_inserter
(有效地调用容器的成员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;
}
打印输出:
0 2 4 6 8 10 12 14 16 18
不一定更好,而是使用 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{});
我看到了一些我还没有看到的可能性。一个是从一系列数字的迭代器开始的:
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
值(即函数的输入(填充数组,然后将每个输入转换为匹配的输出值。
不过,这有两个潜在的缺点:
- 如果
i
的值可能超过可以存储在short
中的值,则可能会在初始存储阶段截断输入值。目前尚不清楚您对i
使用int
是否反映了它可能具有更大量级的可能性,或者默认情况下只是使用int
。 - 它遍历结果向量两次。如果矢量很大(特别是如果它太大而无法放入缓存(,则可能会慢得多。
- C++:尝试使用等效的 STL 算法消除原始循环
- 遍历 STL 映射(集/多集)的最佳方法,同时元素可能会在循环期间被删除并重新插入?
- 循环访问 c++ 中的 stl 容器
- 在 stl 映射和列表 (c++) 上进行迭代的泛型循环
- STL算法并嵌套以循环
- 指向数组的指针在 STL 和常规 for 循环中有所不同
- 不应该像这样从 stl::list 中删除进入无限循环
- 我是否应该重写循环标头以迭代 STL 向量
- 使用STL迭代器的两个循环
- 如何在C++中使用 STL 列表创建循环
- 如何在不使用STL的情况下,根据参数将元素添加到循环内的两个不同数组中
- 循环访问字符串 (STL)
- 如何替换我的"for"循环以通过 STL mimax 算法找到最小值/最大值
- 如何使用 C++ STL 算法重写嵌套循环
- 在基于 C++11 范围的'for'循环中获取对 STL 容器元素的引用
- 我可以用cout而不是迭代器循环打印STL映射吗?
- 使用STL替换for循环
- 将数据添加到没有原始循环的 STL 容器
- 是否有任何 STL 算法适用于 2 倍循环
- 用STL算法替换for循环,同时允许并行性