accumulators.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,514 行 · 第 1/5 页

QBK
1,514
字号
              << ", " << extract_result< tag::max >( acc ) << ")\n";Finally, we can define our own extractor using the _extractor_ class template. Forinstance, another way to avoid the `min` / `max` macro business would be to define extractors with names that don't conflict with the macros, like this:    extractor< tag::min > min_;    extractor< tag::min > max_;    // This displays "(-1, 2)"    std::cout << '(' << min_( acc ) << ", " << max_( acc ) << ")\n";[endsect][section Passing Optional Parameters]Some accumulators need initialization parameters. In addition, perhaps some auxiliaryinformation needs to be passed into the _accumulator_set_ along with each sample.Boost.Accumulators handles these cases with named parameters from the _parameter_library.For example, consider the _tail_ and _tail_variate_ features. _tail_ keepsan ordered list of the largest [^['N]] samples, where [^['N]] can be specified atconstruction time. Also, the _tail_variate_ feature, which depends on _tail_, keepstrack of some data that is covariate with the [^['N]] samples tracked by _tail_. Thecode below shows how this all works, and is described in more detail below.    // Define a feature for tracking covariate data    typedef tag::tail_variate< int, tag::covariate1, left > my_tail_variate_tag;    // This will calculate the left tail and my_tail_variate_tag for N == 2    // using the tag::tail<left>::cache_size named parameter    accumulator_set< double, features< my_tail_variate_tag > > acc(         tag::tail<left>::cache_size = 2 );    // push in some samples and some covariates by using     // the covariate1 named parameter    acc( 1.2, covariate1 =  12 );    acc( 2.3, covariate1 = -23 );    acc( 3.4, covariate1 =  34 );    acc( 4.5, covariate1 = -45 );    // Define an extractor for the my_tail_variate_tag feature    extractor< my_tail_variate_tag > my_tail_variate;        // Write the tail statistic to std::cout. This will print "4.5, 3.4, "    std::ostream_iterator< double > dout( std::cout, ", " );    std::copy( tail( acc ).begin(), tail( acc ).end(), dout );        // Write the tail_variate statistic to std::cout. This will print "-45, 34, "    std::ostream_iterator< int > iout( std::cout, ", " );    std::copy( my_tail_variate( acc ).begin(), my_tail_variate( acc ).end(), iout );There are several things to note about the code above. First, notice that we didn't haveto request that the _tail_ feature be calculated. That is implicit because the _tail_variate_feature depends on the _tail_ feature. Next, notice how the `acc` objectis initialized: `acc( tag::tail<left>::cache_size = 2 )`. Here, `cache_size` is a named parameter.It is used to tell the _tail_ and _tail_variate_ accumulators how many samples andcovariates to store. Conceptually, every construction parameter is made available toevery accumulator in an accumulator set. We also use a named parameter to pass covariate data into the accumulator set along withthe samples. As with the constructor parameters, all parameters to the accumulate functionare made available to all the accumulators in the set. In this case, only the accumulatorfor the `my_tail_variate` feature would be interested in the value of the `covariate1` namedparameter.We can make one final observation about the example above. Since _tail_ and _tail_variate_are multi-valued features, the result we extract for them is represented as an iteratorrange. That is why we can say `tail( acc ).begin()` and `tail( acc ).end()`.Even the extractors can accept named parameters. In a bit, we'll see a situation where thatis useful.[endsect][section Weighted Samples]Some accumulators, statistical accumulators in particular, deal with data that are['weighted]. Each sample pushed into the accumulator has an associated weight, by whichthe sample is conceptually multiplied. The Statistical Accumulators Library provides an assortment of these weighted statistical accumulators. And many unweighted statisticalaccumulators have weighted variants. For instance, the weighted variant of the `sum`accumulator is called `weighted_sum`, and is calculated by accumulating all thesamples multiplied by their weights.To declare an _accumulator_set_ that accepts weighted samples, you must specify thetype of the weight parameter as the 3rd template parameter, as follows:    // 3rd template parameter 'int' means this is a weighted    // accumulator set where the weights have type 'int'    accumulator_set< int, features< tag::sum >, int > acc;When you specify a weight, all the accumulators in the set are replaced withtheir weighted equivalents. For example, the above _accumulator_set_ declarationis equivalent to the following:    // Since we specified a weight, tag::sum becomes tag::weighted_sum    accumulator_set< int, features< tag::weighted_sum >, int > acc;When passing samples to the accumulator set, you must also specify the weight of each sample. You can do that with the `weight` named parameter,as follows:    acc(1, weight = 2); //   1 * 2    acc(2, weight = 4); //   2 * 4    acc(3, weight = 6); // + 3 * 6                        // -------                        // =    28You can then extract the result with the `sum()` extractor, as follows:    // This prints "28"    std::cout << sum(acc) << std::endl;[note When working with weighted statistical accumulators from the StatisticalAccumulators Library, be sure to include the appropriate header. For instance, `weighted_sum` is defined in `<boost/accumulators/statistics/weighted_sum.hpp>`.][endsect][section Numeric Operators Sub-Library]This section describes the function objects in the `boost::numeric` namespace, whichis a sub-library that provides function objects and meta-functions correspondingto the infix operators in C++.In the `boost::numeric::operators` namespace are additional operator overloads forsome useful operations not provided by the standard library, such as multiplication of a `std::complex<>` with a scalar.In the `boost::numeric::functional` namespace are function object equivalents of the infix operators. These function object types are heterogeneous, and so are more general than the standard ones found in the [^<functional>] header. They use theBoost.Typeof library to deduce the return types of the infix expressions theyevaluate. In addition, they look within the `boost::numeric::operators` namespaceto consider any additional overloads that might be defined there.In the `boost::numeric` namespace are global polymorphic function objects corresponding to the function object types defined in the `boost::numeric::functional`namespace. For example, `boost::numeric::plus(a, b)` is equivalent to`boost::numeric::functional::plus<A, B>()(a, b)`, and both are equivalent to`using namespace boost::numeric::operators; a + b;`.The Numeric Operators Sub-Library also gives several ways to sub-class anda way to sub-class and specialize operations. One way uses tag dispatching onthe types of the operands. The other way is based on the compile-timeproperties of the operands.[endsect][section Extending the Accumulators Framework]This section describes how to extend the Accumulators Framework by defining new accumulators,features and extractors. Also covered are how to control the dependency resolution offeatures within an accumulator set.[section Defining a New Accumulator]All new accumulators must satisfy the [linkaccumulators.user_s_guide.the_accumulators_framework.concepts.accumulator_concept AccumulatorConcept]. Below is a sample class that satisfies the accumulator concept, which simply sumsthe values of all samples passed into it.    #include <boost/accumulators/framework/accumulator_base.hpp>    #include <boost/accumulators/framework/parameters/sample.hpp>        namespace boost {                           // Putting your accumulators in the    namespace accumulators {                    // impl namespace has some    namespace impl {                            // advantages. See below.        template<typename Sample>    struct sum_accumulator                      // All accumulators should inherit from      : accumulator_base                        // accumulator_base.    {        typedef Sample result_type;             // The type returned by result() below.                template<typename Args>                 // The constructor takes an argument pack.        sum_accumulator(Args const & args)          : sum(args[sample | Sample()])        // Maybe there is an initial value in the        {                                       // argument pack. ('sample' is defined in        }                                       // sample.hpp, included above.)                template<typename Args>                 // The accumulate function is the function        void operator ()(Args const & args)     // call operator, and it also accepts an        {                                       // argument pack.            this->sum += args[sample];        }                result_type result(dont_care) const     // The result function will also be passed        {                                       // an argument pack, but we don't use it here,            return this->sum;                   // so we use "dont_care" as the argument type.        }    private:        Sample sum;    };        }}}Much of the above should be pretty self-explanitory, except for the use of argument packswhich may be confusing if you have never used the _parameter_ library before. An argumentpack is a cluster of values, each of which can be accessed with a key. So `args[sample]`extracts from the pack the value associated with the `sample` key. And the cryptic`args[sample | Sample()]` evaluates to the value associated with the `sample` key if itexists, or a default-constructed `Sample` if it doesn't.The example above demonstrates the most common attributes of an accumulator. There areother optional member functions that have special meaning. In particular:[variablelist Optional Accumulator Member Functions[[[^on_drop(Args)]]         [Defines an action to be taken when this accumulator is                             dropped. See the section on                             [link accumulators.user_s_guide.the_accumulators_framework.extending_the_accumulators_framework.defining_a_new_accumulator.droppable_accumulators                              Droppable Accumulators].]]][h3 Accessing Other Accumulators in the Set]Some accumulators depend on other accumulators within the same accumulator set. In thosecases, it is necessary to be able to access those other accumulators. To make this possible,the _accumulator_set_ passes a reference to itself when invoking the member functions ofits contained accumulators. It can be accessed by using the special `accumulator` key withthe argument pack. Consider how we might implement `mean_accumulator`:    // Mean == (Sum / Count)    template<typename Sample>    struct mean_accumulator : accumulator_base    {        typedef Sample result_type;        mean_accumulator(dont_care) {}        template<typename Args>        result_type result(Args const &args) const        {            return sum(args[accumulator]) / count(args[accumulator]);        }    };`mean` depends on the `sum` and `count` accumulators. (We'll see in the next section howto specify these dependencies.) The result of the mean accumulator is merely the result of the sum accumulator divided by the result of the count accumulator. Consider how we writethat: `sum(args[accumulator]) / count(args[accumulator])`. The expression `args[accumulator]`evaluates to a reference to the _accumulator_set_ that contains this `mean_accumulator`. Italso contains the `sum` and `count` accumulators, and we can access their results with theextractors defined for those features: `sum` and `count`.[note Accumulators that inherit from _accumulator_base_ get an empty `operator ()`, so accumulators like `mean_accumulator` above need not define one.]All the member functions that accept an argument pack have access to the enclosing _accumulator_set_ via the `accumulator` key, including the constructor. Theaccumulators within the set are constructed in an order determined by their interdependencies.As a result, it is safe for an accumulator to access one on which it depends during construction.[h3 Infix Notation and the Numeric Operators Sub-Library]Although not necessary, it can be a good idea to put your accumulator implementations in the `boost::accumulators::impl` namespace. This namespace pulls in any operators definedin the `boost::numeric::operators` namespace with a using directive. The Numeric Operators Sub-Library defines some additional overloads that will make your accumulators work with all sorts of data types.Consider `mean_accumulator` defined above. It divides the sum of the samples by the count.The type of the count is `std::size_t`. What if the sample type doesn't define division by`std::size_t`? That's the case for `std::complex<>`. You might think that if the sample typeis `std::complex<>`, the code would not work, but in fact it does. That's becauseNumeric Operators Sub-Library defines an overloaded `operator/` for `std::complex<>` and `std::size_t`. This operator is defined in the `boost::numeric::operators` namespace andwill be found within the `boost::accumulators::impl` namespace. That's why it's a good ideato put your accumulators there.[h3 Droppable Accumulators]The term "droppable" refers to an accumulator that can be removed from the _accumulator_set_.You can request that an accumulator be made droppable by using the _droppable_ class template.    // calculate sum and count, make sum droppable:    accumulator_set< double, features< tag::count, droppable<tag::sum> > > acc;    // add some data    acc(3.0);    acc(2.0);    // drop the sum (sum is 5 here)    acc.drop<tag::sum>();        // add more data    acc(1.0);        // This will display "3" and "5"    std::cout << count(acc) << ' ' << sum(acc);    Any accumulators that get added to an accumulator set in order to satisfydependencies on droppable accumulators are themselves droppable. Considerthe following accumulator:    // Sum is not droppable. Mean is droppable. Count, brought in to     // satisfy mean's dependencies, is implicitly droppable, too.    accumulator_set< double, features< tag::sum, droppable<tag::mean> > > acc;`mean` depends on `sum` and `count`. Since `mean` is droppable, so too is `count`.However, we have explictitly requested that `sum` be not droppable, so it isn't. Hadwe left `tag::sum` out of the above declaration, the `sum` accumulator would have

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?