Zastanawiam się, czy możliwe jest przekonwertowanie prostej pętli, która jest wywoływana przez pakiet parametrów do constexpr z prostszym kodem. Ten przykładowy kod pokazuje, co próbuję zrobić

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

Zwany z

 Student s;
 calculate_class_probability(class_summaries, s , &Student::age, &Student::income, &Student::credit_rating, &Student::is_student);
1
Delta_Fore 16 luty 2017, 20:36

2 odpowiedzi

Najlepsza odpowiedź

Niekoniecznie sprawia, że kod jest krótszy jako całość, ale rozdziela ogólną część, którą można łatwo ponownie wykorzystać, a ImHO sprawia, że kod jest wyraźniejszy. Kluczem w tym konkretnym przypadku jest funkcją, która mapuje pakiety w tablicach określonego typu:

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

W twoim przypadku nie jest to wystarczająco dużo, jak chcesz go zipować z wektorem. Więc przydatna modyfikacja tego byłaby w celu uzyskania indeksu całkowitego elementu opakowego dostępnego i przekazać go do funkcji:

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

Teraz możesz użyć tej funkcji, tak jak:

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; });
}
1
Nir Friedman 16 luty 2017, 18:14

Nie wiesz, aby zrozumieć, czego chcesz, ale przypuszczam, że możesz wyrzucić swoje getProbability() i przepisać calculate_class_probability() w następujący sposób

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

Ale: Nie: Nie sądzę, aby pisać to jako constexpr; operator[] dla std::vector<> nie jest constexpr

0
max66 16 luty 2017, 19:18