سؤال

I am implementing the following data set and am wanting to avoid the lengthy operational notation typically involved, such as . and -> selectors, etc.

struct myList {
    double node1 = 2,
    node2 = 4,
    node3 = 6.6,
    node4 = 8
};

How can I arbitrarily sum the nodes with non-specific selection. For example with jQuery, if this structure was JSON, then I could do something like this:

$(myList).children().each(function() {
    sum += Number($(this).val());
});


// Or more generic, given size..
for (var i = 0; i < NUM_MEM; i++) {
    sum += $(myList).nth-child(i).val();
}
هل كانت مفيدة؟

المحلول

Use an array, a vector (or list) or a map, depending on what is most practical.

An array is inflexible, fixed size at compile to. A std::vector<double> is flexible and easy to use. For example:

#include <iostream>
#include <numeric>  // for std::accumulate, also look at <algorithms>
#include <vector>

struct data {
    std::vector<double> nodes;
};

int main() {
    data d = {{1, 2, 3}};
    double sum = std::accumulate(std::begin(d.nodes), std::end(d.nodes), 0);
    std::cout << "Total: " << sum << std::endl;
}

Use a std::map<std::string, double> (or std::unordered_map) if you would prefer to use names rather than indexes.

#include <iostream>
#include <map>
#include <numeric>
#include <string>

struct data {
    std::map<std::string, double> items;
};

typedef std::map<std::string, double>::value_type my_map_element;

int main() {
    data d = {{{"item1", 1}, {"item2", 2}, {"item3", 42}}};
    double sum = std::accumulate(std::begin(d.items), std::end(d.items), 0,
            [](double partial, my_map_element const& elt) {
                  return partial+elt.second;
            });
            //[](auto partial, auto const& elt) { // C++14
            //      return partial+elt.second;
            //});
    std::cout << "Total: " << sum << std::endl;
}

std::accumulate, like most standard algorithms, takes iterators. You'll find that all over the place in generic C++ libraries. This makes the algorithms very flexible - they don't depend on the actual data structure being used.

If you can't change the data classes, you can use simple wrappers to make them iterable (one type at a time anyway) without much trouble using std::reference_wrapper which behaves pretty much like a reference, but can be stored in collections (and arrays).

Here's an example of how that could work:

#include <functional> // for std::reference_wrapper
#include <iostream>
#include <numeric>    // for std::accumulate
#include <vector>

struct data
{
    double node1, node2, node3;
};

int main()
{
    typedef std::vector<std::reference_wrapper<double>> double_wrapper;

    data d { 1, 2, 3 };
    double_wrapper helper = { d.node1, d.node2, d.node3 };

    // You update via the helper or directly
    d.node1 = 4;
    helper[1].get() = 5; // will update d.node2

    // Handy iteration
    for (auto& node: helper)
        node.get()++;

    // And use standard algorithms
    std::cout
            << "Sum: "
            << std::accumulate(std::begin(helper), std::end(helper), 0)
            << std::endl;
}

Warning though: the lifetime of the helper/wrapper is deeply tied to the lifetime of the data object itself. Don't let the wrapper outlive the data object. If you "repackage" the external data and something like the vector above in a struct for your internal use, also be very careful about copy (and move) semantics, for the same reason.

نصائح أخرى

As long as you keep all your types the same, all struct access specifiers the same, have no virtual functions and all your types are discrete types, this is guaranteed by the standard to work.

It's a bit of a horror show, but then introspection always was...

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

struct myList {
    double node1 = 2,
    node2 = 4,
    node3 = 6.6,
    node4 = 8,
    myListEnd = -1;

    const double* nthChild(size_t i) const {
        return &node1 + i;
    }

    const double* begin() const {
        return &node1;
    }

    const double* end() const {
        return &myListEnd;
    }
};

struct deref {

    deref& operator+=(double a) {
        _tot += a;
        return *this;
    }
    operator double() const { return _tot; }
    double _tot = 0;
};

deref operator+(deref d, double a) {
    d += a;
    return d;
}


using namespace std;


int main()
{
    myList mine;

    auto sum = accumulate(&mine.node1, &mine.myListEnd, deref());
    cout << "sum is " << sum << endl; 

    sum = accumulate(mine.begin(), mine.end(), deref());
    cout << "sum is " << sum << endl; 

    sum = accumulate(mine.nthChild(1), mine.nthChild(3), deref());
    cout << "slice sum is " << sum << endl; 


   return 0;
}

outputs:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
sum is 20.6
sum is 20.6
slice sum is 10.6
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top