2017-02-16 18 views
1

Мне интересно, можно ли преобразовать простой цикл, который вызывается через пакет параметров в constexpr с более простым кодом. Этот пример кода показывает, что я пытаюсь сделатьПреобразование вариационного шаблона в constexpr

struct Student { 
    AgeCategory age; 
    Income income; 
    bool is_student; 
    CreditRating credit_rating; 
    bool buys_computer; 
}; 

auto find_probability(const double x, const double mean, const double stdev) -> double; 

typedef std::tuple<double, double> MeanStdDev; 
typedef std::vector<MeanStdDev> MeanStdDevVec; 


// This code seems verbose to me. Is there a simpler way to express this 
// loop which iterates over a vector and parameter pack, possibly 
// using constexpr. C++14/17 idioms are fine. 
template<typename Attr> 
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, Attr attr) -> double { 
    double mean, stdev; 
    std::tie(mean, stdev) = v[n]; 

    return find_probability(static_cast<double>(std::invoke(attr, s)), mean, stdev); 
} 

template<typename First, typename... Attr> 
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, First f, Attr... attr) -> double { 
    double mean, stdev; 
    std::tie(mean, stdev) = v[n]; 

    return find_probability(static_cast<double>(std::invoke(f,s)), mean, stdev) * get_probability(v, n + 1, s, attr...); 
} 

template<typename ...Attr> 
auto calculate_class_probability(const std::map<bool, MeanStdDevVec>& summaries, const Student& s, Attr... attributes) { 
    for (const auto& i : summaries) { 
     get_probability(i.second, 0L, s, attributes...); 
    } 
} 

вызывается из

Student s; 
calculate_class_probability(class_summaries, s , &Student::age, &Student::income, &Student::credit_rating, &Student::is_student); 

ответ

1

Это не обязательно делает код короче в целом, но это выделить в общей части, что вам может повторно использоваться, и IMHO делает код более понятным. Ключевым в данном случае является функцией, которая отображает пакеты в массивы определенного типа:

template <class T, class F, class ... Args> 
std::array<T, sizeof...(Args)> pack_to_array(F f, Args&& ... args) { 
    return {(f(std::forward<Args>(args)))...}; 
} 

В вашем случае, это не вполне достаточно, как вы хотите, чтобы пронестись его с вектором. Так полезная модификация этого, было бы сделать целочисленный индекс элемента пакета доступны и передать его функции:

template <class T, class F, class ... Args> 
std::array<T, sizeof...(Args)> index_pack_to_array(F f, Args&& ... args) { 
    std::size_t i = 0; 
    return {(f(i++, std::forward<Args>(args)))...}; 
} 

Теперь вы можете использовать эту функцию, например, так:

template<typename... Attr> 
double get_probability(const MeanStdDevVec& v, const Student& s, Attr... attr) { 

    assert(v.size() == sizeof...(Attr)); 
    auto probs = index_pack_to_array<double>(
     [&] (std::size_t i, auto&& a) -> double { 
      return // ... (get probability from v[i], s, and a) 
     }, 
     std::forward<Attr>(attr)...); 

    return std::accumulate(probs.begin(), probs.end(), 1.0, 
     [] (double p1, double p2) { return p1 * p2; }); 
} 
+0

Это довольно умно, поэтому спасибо – Ronnie

+0

@Ronnie Спасибо! Если вам кажется, что он отвечает на ваш вопрос, отметьте его принятым (нажмите галочку). Приветствия. –

0

не уверены, чтобы понять, что вы хотите, но я полагаю, вы можете выбросить свой getProbability() и переписать calculate_class_probability() следующим

template <typename ... Attr> 
auto calculate_class_probability 
    (std::map<bool, MeanStdDevVec> const & summaries, 
     Student const & s, Attr ... attributes) 
{ 
    using unusedT = int[]; 

    for (const auto & i : summaries) 
    { 
     double  mean, stdev; 
     double  prob {1.0}; 
     std::size_t n {0}; 

     (void)unusedT { (std::tie(mean, stdev) = i.second[n++], 
         prob *= find_probability(static_cast<double>(std::invoke(attributes,s)), mean, stdev), 
         0)... }; 

     // in prob the calculate value 
    } 
} 

но: нет: я не думаю, что это возможно Wr ite это как constexpr; operator[] для std::vector<> не является constexpr