Boost: вектор распределения с любым

Уважаемые эксперты по обмену стека,

Я пытаюсь настроить класс (многомерная функция распределения), которая хранит импульсные распределения в std :: vector (предельные функции распределения).
Хотя это возможно, используя boost :: Вариант (см. Мой вопрос: Boost: хранить указатели на распределения в векторе), Я также дал boost :: any попробовать.
Причина в том, что с вариантом я должен жестко кодировать потенциальные типы (предельные распределения) при настройке варианта, и я хотел избежать этого.

Хотя различные реализованные классы распределения не имеют общего родительского класса, существуют функции, такие как boost :: math :: cdf или boost :: math :: pdf, которые можно применять ко всем дистрибутивам, и которые я хочу применить для итерации std :: vector.

Работая с любым, я произвел код ниже (который работает нормально), но теперь у меня есть проблема, что функция any_cdf должна проверять типы.

Несмотря на то, что я обошел жесткое кодирование типов при настройке вектора (как для варианта), теперь мне нужно жестко кодировать типы в функции any_cdf (в то время как решение с вариантами может обрабатывать применение функции cdf через шаблонную функцию посетителя). и, следовательно, без каких-либо спецификаций типов), что означает много кода для управления, много операторов if …

Тем не менее, логика не меняется вообще (я приводил тип, затем применяю функцию cdf ко всем операторам if), и мне было бы все равно, как ведет себя функция, если в списке сохраняется что-то отличное от буст-распределения.

Так есть ли шанс получить мой торт и съесть его, то есть не заставлять жестко кодировать тип распределения дистрибутива в any_cdf (очень похоже на шаблонную функцию посетителя для вариантов)?

Большое спасибо за вашу помощь, Х.

Постскриптум если это не осуществимо, лучше ли мне в этом случае использовать boost :: any или boost :: option?

#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, как посетитель:
шаблон посетителей для повышения :: любой

1

Решение

Обновить Это ответ, предполагающий вектор и boost::any против boost::variant, Если вы можете использовать tuple<> см мой другой ответ

Вы закончите тем, что жестко закодируете потенциальные типы так или иначе.

С вариантом, вы можете сгруппировать и скрыть сложности с помощью посетителя:

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);

С любой надстройкой вы бы делали переключение с клавиатуры скучным и ручным способом:

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();

ИМО это явно уступает в обслуживании, например

  • Вы не можете легко расширить список типов с типом элемента, который достаточно похож, чтобы удовлетворить ту же концепцию и иметь его поддержку — вам нужно будет вручную добавлять случаи в переключатель типа (а если нет — вы ‘ Не повезло, ошибки нет, и у вас будут (тихие) ошибки. С variant вы просто получите ошибку компиляции, когда ваш посетитель не обрабатывает ваш тип.

  • эта работа ^ (переключение типов) дублируется для каждой операции, которую вы хотите реализовать по всем направлениям. Конечно, вы можете реализовать переключение типов один раз и предоставить фактическую реализацию как функтор, но в тот момент вы уже реализовали точный эквивалент из static_visitor как я показал для варианта, за исключением гораздо менее эффективной реализации.

  • boost::any может содержать только те значения, которые CopyConstructible, Увеличение variant может даже содержать ссылки (например, 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);
}

Полноценную программу можно найти Жить на Колиру

Демо-листинг

#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; это просто потребовало бы больше времени, чем я в настоящее время должен бросить в это.

2

Другие решения

Заметка Увидеть мой старый ответ для обсуждения решений вектора и boost::any против boost::variant,

Если вам на самом деле не нужен динамический вектор распределений — вы просто хотите применить статически известный список распределений, вы можете «уйти» с помощью tuple<> из них.

Теперь, используя немного (ну, много) магии от Phoenix и Fusion, вы можете «просто» адаптировать cdf функционировать как Ленивый Актер:

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

В каком случае эквивалент расширенный Пример кода сокращается до: увидеть его Жить на Колиру

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;
}

Whoah. Это … вряд ли 6 строк кода 🙂 И лучшая часть все это совместимо с C ++ 03 уже.

3

Как насчет:

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;
}

Привязка аргумента к шаблону функции может быть сложной задачей.

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector