Boost:任意分布的向量
Boost: Vector of Distributions with any
亲爱的栈交换专家,
我试图建立一个类(多元分布函数),将boost分布存储在std::向量(边际分布函数)中。虽然这可以使用boost::variant(参见我的问题:boost:在Vector中存储指向分布的指针),但我也尝试了boost::any。原因是,在设置变体时,我必须硬编码潜在的类型(边际分布),我想避免这种情况。
虽然不同实现的发行版类没有共享一个共同的父类,但有一些函数,如boost::math::cdf或boost::math::pdf,可以应用于所有发行版,我想在std::vector上应用迭代。
我生成了下面的代码(运行良好),但是现在我有一个问题,函数any_cdf需要检查类型。虽然我在设置向量时避免了硬编码类型(对于变体),但我现在需要硬编码any_cdf函数中的类型(而变体的解决方案可以通过模板化的访问者函数处理cdf函数的应用程序,因此没有任何类型规范),这意味着要管理大量代码,大量if语句…
然而,逻辑根本没有改变(我转换类型,然后在所有if语句中应用cdf函数),如果列表中存储的不是boost分布,我也不会真正关心函数的行为。
那么是否有机会拥有我的蛋糕并吃掉它,这意味着不被迫硬编码any_cdf中的分布的强制类型(很像变体的模板化访问者函数)?
非常感谢你的帮助,H.
注。如果这是不可行的,那么在这种情况下,我通常使用boost::any或boost::变体会更好吗?
#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;
}
编辑:关于评论,any似乎不是手头任务最简单/最好的选择。然而,出于完整性的考虑,boost::any的类似访问者的实现在:注释参见我的旧答案关于向量和
boost::any
与boost::variant
的解的讨论。
如果你实际上不需要分布的动态向量,而只是想应用一个静态已知的分布列表,你可以"摆脱"它们的tuple<>
。
现在,用一点(嗯,很多)来自凤凰和融合的魔法,你可以"仅仅"适应cdf
函数作为一个懒惰演员:
BOOST_PHOENIX_ADAPT_FUNCTION(double, cdf_, boost::math::cdf, 2)
在这种情况下,等效的扩展代码示例缩小为:查看它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):tt" << boost::fusion::accumulate(dists, 0.0, arg1 + cdf_(arg2, x)) / boost::tuples::length<Dists>::value;
}
哇。这是……不到6行代码:)最好的部分是它已经完全兼容c++03了
Update这是假设一个向量和
boost::any
vs.boost::variant
的答案。如果您可以使用tuple<>
,请参阅我的其他答案
你最终会以这样或那样的方式硬编码潜在的类型。
使用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();
在我看来,这显然是较差的可维护性,例如
你不能很容易地用一个足够相似的元素类型来扩展类型列表,以满足相同的概念并获得它的支持——你需要手动向类型切换添加大小写(如果你不这样做——你就不走运了,没有错误,你会有(无声的)bug。使用
variant
,当你的访问者不处理你的类型时,你会得到一个编译错误。这个工作^(类型切换)对于你想要实现的每个操作都是重复的。当然,您可以实现类型切换一次,并提供作为函子的实际实现,但此时您将实现与
static_visitor
完全相同的,就像我在变体中展示的那样,除了实现效率要低得多。boost::any
只能包含CopyConstructible
。Boostvariant
甚至可以包含引用(例如boost::variant<dist1_t&, dist2_t&>
),并且具有(一些)移动语义支持
简而言之,boost::any
节省了预先考虑的时间,但它所做的只是将工作转移到调用站点。
从积极的方面来说,让我和你分享一个我喜欢的习语,它使访问者作为普通的免费函数来访问。让我们为以下变量重写any_cdf
函数:
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);
}
一个完整的运行程序可以找到Live On Coliru
演示清单#include <boost/bind.hpp>
#include <boost/math/distributions.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <limits>
#include <vector>
namespace detail
{
template <typename T> struct var_cdf_visitor : boost::static_visitor<T> {
template <typename Dist>
T operator()(Dist const& dist, T const& x) const { return boost::math::cdf(dist, x); }
};
}
template<class T, typename... Dist> T var_cdf(boost::variant<Dist...> const& a, T const& x) {
return boost::apply_visitor(boost::bind(detail::var_cdf_visitor<T>(), ::_1, x), a);
}
int main()
{
namespace bm = boost::math;
typedef std::vector<boost::variant<bm::normal, bm::students_t> > Vec;
Vec vec { bm::normal(), bm::students_t(1) };
//evaluation point and return value
double x = 1.96;
for (auto& dist : vec)
std::cout << var_cdf(dist,x) << std::endl;
}
实际上,虽然我使用了一点c++11,但使用一些c++1y特性(如果你的编译器有的话)可以使它更漂亮。
最后,你也可以为c++03工作;只是需要的时间比我现在能投入的时间要多。
关于:
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;
}
将参数绑定到函数模板可能很棘手。
- 写入向量<向量<bool>>
- 函数向量_指针有不同的原型,我可以构建一个吗
- std::向量与传递值的动态数组
- 将值指定给向量(2D)的向量中的某个位置
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 如何使用向量的template_back函数
- 尝试通过多个向量访问变量时,向量下标超出范围
- 如何通过派生类函数更改基类中的向量
- C++从另一个类访问公共静态向量的正确方法是什么
- 如何将ampl中的集合表示为c++中的向量
- 变量没有改变?通过向量的函数调用
- 迭代时从向量和内存中删除对象
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- 向量中布尔值的均匀分布
- 如何在向量中保存不同的分布
- 计算整数向量的频率分布
- 数据在向量中的均匀分布
- C++中具有概率分布的向量的随机生成器
- Boost:任意分布的向量
- 返回Boost分布生成器从函数填充向量在c++中