我想要的功能就像:
std::vector<float> GetFuncVec(int N, FuncType type)
{
std::vector<float> fn(N);
float tmp = (N - 1) / 2.0;
switch (type) {
case SIN:
for (int i=0; i<N; ++i)
fn[i] = sin(M_PI * i / tmp);
break;
case SINC:
for (int i=0; i<N; ++i)
fn[i] = sin(M_PI * i / tmp) / (M_PI * i / tmp);
break;
...
}
return fn;
}
我发现这不令人满意,因为有很多代码重复。环顾四周,我找到了STL算法std::generate()
,它可以使用仿函数填充向量,该仿函数可以有一个增量成员来扮演i
的角色。
我看到两条潜在的路线。第一种是使用工厂初始化仿函数。这种方法的问题是代码分离(上面,不同的情况很好地保存在一起)和增加的开销需要多个新类。
第二个是使用lambda函数(我很少有经验)。这很好,因为我可以在switch语句中的单行中定义每个函数。但是我没有看到如何避免范围问题(lambda函数在switch语句的范围之外是不可访问的)。
是否有使用lambda函数的解决方案?从效率角度和可读性角度来看,最佳选择是什么?
答案 0 :(得分:2)
也许你想要这样的东西......? (见它运行here
#include <iostream>
#include <vector>
#include <cmath>
#include <functional>
enum Func { Sin, Sinc };
std::vector<float> f(int n, Func func)
{
std::vector<float> results(n);
float tmp = (n - 1) / 2.0;
int i;
std::function<float()> fns[] = {
[&] { return sin(M_PI * i / tmp); },
[&] { return sin(M_PI * i / tmp) / (M_PI * i / tmp); }
};
auto& fn = fns[func];
for (i=0; i<n; ++i)
results[i] = fn();
return results;
}
int main()
{
std::vector<float> x = f(10, Sin);
for (auto& v : x) std::cout << v << ' '; std::cout << '\n';
std::vector<float> y = f(10, Sinc);
for (auto& v : y) std::cout << v << ' '; std::cout << '\n';
}
输出:
0 0.642788 0.984808 0.866025 0.34202 -0.34202 -0.866025 -0.984808 -0.642788 -2.44929e-16
-nan 0.920725 0.705317 0.413497 0.122477 -0.0979816 -0.206748 -0.201519 -0.115091 -3.89817e-17
答案 1 :(得分:1)
一个可能不快的选项(每个函数调用都有间接)但创建std::map<FuncType, std::function<float(int,float)>>
会更灵活一些。您不能使用std::generate()
,因为您需要参数i
来计算结果,但编写自己的结果并不难:
template <typename Iterator, typename Generator, typename Index, typename... Args>
void generate_i(Iterator first, Iterator last, Generator gen, Index i, Args... args)
{
while (first != last) {
*first = gen(i, args...);
++i;
++first;
}
}
现在我们有了这个,我们需要填充一个仿函数地图:
using FuncTypeFunction = std::function<float(int,float)>;
using FuncTypeFunctionMap = std::map<FuncType, FuncTypeFunction>;
FuncTypeFunctionMap create_functype_map()
{
FuncTypeFunctionMap functions;
functions[SIN] = [] (int i, float tmp) {
return sin(M_PI * i / tmp);
};
functions[SINC] = [] (int i, float tmp) {
return sin(M_PI * i / tmp) / (M_PI * i / tmp);
};
// ...
return functions;
}
FuncTypeFunctionMap const FuncTypeFunctions = create_functype_map();
(如果您愿意,可以使用boost.assign来提高此位的可读性。)
最后,我们可以使用这张地图:
std::vector<float> GetFuncVec(int N, FuncType type)
{
std::vector<float> fn(N);
float tmp = (N - 1) / 2.0;
auto func = FuncTypeFunctions.find(type);
if (func != FuncTypeFunctions.end()) {
generate_i(fn.begin(), fn.end(), func->second, 0, tmp);
}
return fn;
}
添加新功能只需要在create_functype_map()
中填充地图。请注意,generate_i()
循环中的每次迭代都将调用operator()
上的std::function
,这需要一个间接级别来解析调用,类似于虚方法调用的开销。这会在性能方面花费一些成本,但对您来说可能不是问题。
答案 2 :(得分:1)
您可以编写将在标准算法std::iota
例如
#include <iostream>
#include <functional>
#include <vector>
#include <numeric>
class Value
{
public:
Value() : i( 0 ), fn( []( size_t i ) { return ( float )i; } ) {}
Value & operator ++() { ++i; return *this; }
operator float () const { return fn( i ); }
Value & operator =( std::function<float( size_t )> fn )
{
this->fn = fn;
return *this;
}
private:
size_t i;
std::function<float( size_t )> fn;
};
enum E { First, Second };
std::vector<float> f( size_t N, E e )
{
Value value;
float tmp = N / 2.0f;
switch( e )
{
case First:
value = [tmp] ( size_t i ) { return i * tmp; };
break;
case Second:
value = [tmp] ( size_t i ) { return i * tmp + tmp; };
break;
}
std::vector<float> v( N );
std::iota( v.begin(), v.end(), value );
return v;
}
int main()
{
for ( float x : f( 10, First ) ) std::cout << x << ' ';
std::cout << std::endl;
for ( float x : f( 10, Second ) ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
输出
0 5 10 15 20 25 30 35 40 45
5 10 15 20 25 30 35 40 45 50
当然,您可以使用自己的lambda表达式,其中包含一些数学函数,如sin