使用枚举指定的函数填充矢量

时间:2014-11-12 09:20:56

标签: c++ enums lambda factory

我想要的功能就像:

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函数的解决方案?从效率角度和可读性角度来看,最佳选择是什么?

3 个答案:

答案 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,这需要一个间接级别来解析调用,类似于虚方法调用的开销。这会在性能方面花费一些成本,但对您来说可能不是问题。

See a demo

答案 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