提升:任意分布的向量

时间:2014-03-26 15:41:15

标签: c++ boost vector distribution boost-any

亲爱的Stack Exchange专家,

我正在尝试建立一个类(多变量分布函数),它将boost分布存储在std :: vector(边缘分布函数)中。 虽然这可以使用boost :: variant(参见我的问题:Boost: Store Pointers to Distributions in Vector),但我也尝试了boost :: any。 原因是变量我必须在设置变体时对潜在类型(边缘分布)进行硬编码,我想避免这种情况。

虽然不同的已实现的分发类不共享公共父类,但是有一些函数,例如boost :: math :: cdf或boost :: math :: pdf,可以应用于所有发行版,并且我想要在std :: vector上应用迭代。

使用任何我生成下面的代码(运行正常),但现在我遇到了问题,即any_cdf函数需要检查类型。

虽然我在设置向量时避免了对类型进行硬编码(对于变体)我现在需要对any_cdf函数中的类型进行硬编码(而带变体的解决方案可以通过a处理cdf函数的应用)模板化访问者函数,因此没有任何类型规范)这意味着要管理大量代码,很多if语句......

然而,逻辑根本没有改变(我转换类型,然后在所有if语句中应用cdf函数),如果除了boost分配以外的东西存储在中,我真的不会关心函数的行为清单。

那么有没有机会吃我的蛋糕并且吃它,这意味着没有被迫在any_cdf中硬编码分发的类型(很像变体的模板化访问者函数)?

非常感谢你的帮助,H。

P.S。如果这不可行,在这种情况下,我通常会更好地使用boost :: any或boost :: variant吗?

#include <boost/math/distributions.hpp>
#include <boost/any.hpp>
#include <vector>
#include <iostream>
#include <limits>

//template function to apply cdf
template<class T> T any_cdf(boost::any a, T &x){

    //declare return value
    T y;

    //cast any with hardcoded types
    if (a.type() == typeid(boost::math::normal_distribution<T>)){

    y = boost::math::cdf(boost::any_cast< boost::math::normal_distribution<T> >(a),x);

    } else if (a.type() == typeid(boost::math::students_t_distribution<T>)){

    y = boost::math::cdf(boost::any_cast< boost::math::students_t_distribution<T> >(a), x);

    } else {
    //return NaN in case of failure or do something else (throw exception...)
    y =  std::numeric_limits<T>::quiet_NaN();
    }

    return(y);
}



int main (int, char*[])
{
    //get distribution objects
    boost::math::normal_distribution<double> s;
    boost::math::students_t_distribution<double> t(1);

    //use any to put just any kind of objects in one vector 
    std::vector<boost::any> vec_any;
    vec_any.push_back(s);
    vec_any.push_back(t);

    //evaluation point and return value 
    double y;
    double x = 1.96;

    for (std::vector<boost::any>::const_iterator iter = vec_any.begin(); iter != vec_any.end(); ++iter){

        y = any_cdf<double>(*iter,x);
        std::cout << y << std::endl;

    }

    return 0;
}

编辑:关于评论似乎不是最简单/最好的选择。但是,出于完整性原因,访问者喜欢boost :: any的实现,请参阅: visitor pattern for boost::any

3 个答案:

答案 0 :(得分:3)

  

注意有关向量和boost::anyboost::variant的解决方案的讨论,请参阅 my older answer

如果你真的不需要动态的发行版矢量 - 但只是想要应用一个静态已知的发行版列表,你可以“放弃”#34;有tuple<>个。

现在,有了凤凰城和Fusion的一点(好吧,很多)魔力,你可以&#34;只是&#34;将cdf函数调整为 Lazy Actor

BOOST_PHOENIX_ADAPT_FUNCTION(double, cdf_, boost::math::cdf, 2)

在这种情况下,等效 extended代码示例缩小为:查看 Live On Coliru

int main()
{
    typedef boost::tuple<bm::normal, bm::students_t> Dists;
    Dists dists(bm::normal(), bm::students_t(1));

    double x = 1.96;

    boost::fusion::for_each(dists, std::cout << cdf_(arg1, x) << "\n");

    std::cout << "\nComposite (multiplication):\t" << boost::fusion::accumulate(dists, 1.0, arg1 * cdf_(arg2, x));
    std::cout << "\nComposite (mean):\t\t"         << boost::fusion::accumulate(dists, 0.0, arg1 + cdf_(arg2, x)) / boost::tuples::length<Dists>::value;
}

哇。那个......几乎没有6行代码:)最好的部分是它已经兼容c ++ 03 了。

答案 1 :(得分:2)

  

更新这是假设向量和boost::anyboost::variant的答案。如果您可以使用tuple<> see my other answer

您将最终以某种方式对潜在类型进行硬编码。

使用variant,您可以使用visitor来分组和隐藏复杂性:

struct invoke_member_foo : boost::static_visitor<double>
{
     template <typename Obj, typename... Args> 
        double operator()(Obj o, Args const&... a) const {
            return o.foo(a...);
     }
};

这可以应用于您的变体,如

boost::apply_visitor(invoke_member_foo(), my_variant);

使用boost any,你可以通过无聊和手动方式切换类型:

if (auto dist1 = boost::any_cast<distribution1_t>(&my_any))
    dist1->foo();
else if (auto dist2 = boost::any_cast<distribution2_t>(&my_any))
    dist2->foo();
else if (auto dist3 = boost::any_cast<distribution3_t>(&my_any))
    dist3->foo();

IMO这显然不如可维护性,例如

  • 您无法轻松扩展类型列表,其类型类型足以满足相同的概念并获得支持 - 您需要手动向案例开关添加案例(如果你没有 - 你运气不好,那就没有错误,你会有(沉默的)错误。 variant你只是每当访问者无法处理您的类型时,都会收到编译错误。

  • 这项工作^(类型切换)对于您希望在整个板上实施的每个操作都会重复。 当然,您可以实现一次类型切换,并将实际实现作为仿函数提供,但此时您已经实现了<{> <{1>的完全等效正如我为变体所展示的那样,除了实现效率低得多。

  • static_visitor只能包含boost::any的值。 Boost CopyConstructible甚至可以包含引用(例如variant)并且具有(某些)移动语义支持

简而言之,boost::variant<dist1_t&, dist2_t&>提前节省了时间,但它只是将工作转移到呼叫站点。


从积极的方面来说,让我与您分享一个我喜欢的习语,这使得访问者可以像普通的免费功能一样访问。让我们为变体重写boost::any函数:

any_cdf

可以找到完整运行的程序 Live On Coliru

演示列表

namespace detail
{
    template <typename T> struct var_cdf_visitor : boost::static_visitor<T> {
        template <typename Dist>
            T operator()(Dist& dist, T& x) const { return boost::math::cdf(dist, x); }
    };
}

template<class T> T var_cdf(VarDist<T> a, T &x) 
{
    static detail::var_cdf_visitor<T> vis;
    return boost::apply_visitor(
            boost::bind(vis, ::_1, boost::ref(x)),
            a);
}

实际上,虽然我使用了一些c ++ 11,但使用某些c ++ 1y功能(如果你的编译器有这些功能)可以使它变得更漂亮。

最后,你也可以为c ++ 03工作;它只需要比现在更多的时间来投入它。

答案 2 :(得分:2)

怎么样:

int main (int, char*[])
{
    boost::math::normal_distribution<double> s;
    boost::math::students_t_distribution<double> t(1);

    typedef std::vector<boost::function<double (double)> > vec_t; 
    vec_t vec_func;
    vec_func.push_back(boost::bind(boost::math::cdf<double>, boost::ref(s), _1));
    vec_func.push_back(boost::bind(boost::math::cdf<double>, boost::ref(t), _1));

    //evaluation point and return value 
    double y;
    double x = 1.96;

    for (vec_t::const_iterator iter = vec_func.begin(); iter != vec_func.end(); ++iter){
        y = (*iter)(x);
        std::cout << y << std::endl;
    }

    return 0;
}

将参数绑定到函数模板可能会很棘手。