Effective STL Terms 37

zhaozj2021-02-11  177

Terms 37: Statify the residence with Accumulate or for_each

Sometimes you need to extract the entire interval into a separate number, or, more generally, a separate object. For general information, there is a special purpose algorithm to complete this task, for example, count tells you how many elements in the range, and count_if tells you how many elements meet a judgment. The minimum and maximum values ​​in the interval can be obtained by min_element and max_element.

But sometimes you need to use some custom-defined way to count, and in those cases, you need more flexible things than count, count_if, min_element or max_element. For example, you may want to sum up the string of a container. You may want the product of the number of intervals. You may want the average coordinates of the Point interval. In those cases, you need to count a range, but you need to define what you need statistics. no problem. STL prepared such an algorithm for you, it is called Accumulate. You may not be familiar with Accumulate because, it is not like most algorithms, it does not exist in . Instead, it and the other three "numerical algorithms" are in . (That three other algorithms are inner_product, adjacent_difference, and partial_sum.)

Just like many algorithms, accumulate has two forms. The form of a pair of iterators and initial values ​​can return the interval in the interval of the initial value to be divided into the zone:

List id; // Building a list, put

... // Some Double

Double Sum = Accumulate (ld.begin (), id.end (), 0.0); // Calculates them,

// Starting from 0.0

In this example, note that the initial value is specified as 0.0, not simple 0. This is very important. The type of 0.0 is Double, so Acumulate uses a Double type variable to store the calculated sum. If you write this call:

Double Sum = Accumulate (ld.begin (), id.end (), 0); // calculates them,

// starting from 0;

// This is incorrect!

If the initial value is int 0, an int is used inside an int to save the value it calculated. That int final becomes the return value of Accumulate, and it is used to initialize and variables. This code can be compiled and running, but the value may be wrong. Not to save the true Double's list, it may save all the results added, but convert the results into an int for each addition.

Accumulate only needs to enter an iterator, so you can even use iStream_IstReambuf_iterator (see Terms 29):

Cout << "The sum of the tent on the standard input is" // Print CIN

<< Accumulate (istream_iterator (cin), // Things INT and

iStream_iterator (),

0);

The numerical algorithm is the default behavior of Accumulate. However, when using Another form of Accumulate, there is an initial and value and an arbitrary statistical function, which makes it generally.

For example, consider how to use accumulate to calculate the length of the string in the container. To calculate this, Accumulate needs to know two things. First, in the same, it must know and start. In our example, it is 0. Second, it must know how to update this every time you see a new string. To complete this task, we write a function, it comes with the content of the current and new strings, and return updates: string :: size_type // String :: size_type content

StringLengthsum (string :: size_type susofar, // Please see below

Const string & s

{

Return Sumsofar S.Size ();

}

The function of this function is very simple, but you may find yourself in String :: size_type. Don't be like that. Each standard STL container has a typedef called size_type, which is the type of container metering. For example, this is the return type of the SIZE function of the container. For all standard containers, size_type must be Size_T, but theoretically non-standard STL-compatible container may allow SIZE_TYPE to use a different type (although I spent a lot of time, I want to do it). For standard containers, you can regard the container :: size_type as a singular way of the size_t Write.

StringLENGTHSUM is a representative of the statistical function used by Accmulate. It comes with the next element of the statistical value and interval of the interval, which returns a new statistical value. In general, it means that the function will take different types of parameters. That is what is done here. To the current statistical value (the length of the string that has been seen, the length of the string :: size_type, and the type of element to check is String. Typically in this example, the return type and the first parameter of the function here is the same because it is a updated statistical value (plus the final calculated element).

We can let Accumulate use StringLengthsum like this:

Set ss; // Establish a string container,

... // Do some operations

String :: size_type lengthsum = // sets Lengthsum to

Accumulate (ss.begin (), ss.end (), // ss

0, StringLengthsum); // StringLengthsum results, use 0

/ / As an initial statistical value

Very good, isn't it? The accumulation of numerical intervals is even simpler because we don't have to write your own summary function. We can use standard Multiplies imitation function class:

Vector vf; // Establish a Float container

... // Do some operations

FLOAT product = // Set the product to VF

Accumulate (Vf.Begin (), Vf.end (), // in each element call

1.0, Multiplies ()); // Multiplies with 1.0

/ / As an initial statistical value

The only thing that needs to be careful here is to remember 1 (as a floating point number, not int!) As an initial statistical value, not 0. If we use 0 as a start value, the result will always be 0, because 0 multiplied by anything is also 0, isn't it?

Our last example has some embarrassment. It completes the average of the intervals of Point, and Point looks like this: Struct Point {

Point (Double Initx, Double Inity): x (initx), y (inity) {}

Double X, Y;

}

The sum function should be an object called Pointaverage, but let's take a look at how it is in calling accumulate before we look at PointAverage:

List IP;

...

Point avg = // Spect confession on Point in IP

Accumulate (lp.begin (), lp.end (),

Point (0, 0), POINTAVERAGE ());

Simple and direct, our favorite way. In this example, the initial and values ​​are in the origin Point object, we need to remember that when calculating the average of the intervals, do not consider that point.

PointAverage works by recording the number of points it sees and their X and Y part. Each time you call, it updates the values ​​and returns the average coordinates of the Point currently checked, because it only calls every point in the interval, which calls X and Y and the number of points in the interval, Ignore the initial Point value transmitted to Accumulate, it should be like this:

Class PointAverage:

Public binary_function {// see Terms 40

PUBLIC:

Pointaverage (): Xsum (0), YSUM (0), Numpoints (0) {}

Const Point Operator () (Const Point & Avgsofar, Const Point & P) {

Numpoints;

XSUM = P.x;

YSUM = P.y;

Return Point (Xsum / Numpoints, Ysum / Numpoints);

}

Private:

SIZE_T NUMPOINTS;

Double XSUM;

Double Ysum;

}

This is working very well, and only because I am sometimes dealing with some very fanatical people (many of them are in the Standards Committee), so I will prefer the STL implementation it may fail. However, PointAverage and standard sections 26.4.1 paragraph 2 conflict, I know that you think of it, it is to prohibit the passage of the ACCUMLATE. Member variables Numpoints, XSUM and YSUM modifications have caused a side effect, so in terms of technical speaking, the code I just showcase will result in outstanding results. In fact, it is hard to imagine that it can't work, but when I wrote this, I was surrounded by an sinister language lawyer, so I have no choice, I can only write an unknown provision on this issue.

That's good, because it gives me an opportunity to lift for_each, another algorithm that can be used for statistical intervals and there is no more restrictions. As accumulate, for_each has a range and a function (typically a function object) to invoke each element in the interval, but the function transmitted to the for_each receives only one parameter (current interval element), and when FOR_EACH returns Its function. (In fact, it returns a copy of its function - see Terms 38.) It is worth noting that the pass (and later returned) FOR_EACH function may have side effects.

In addition to side effects, the difference between for_each and accumulate is mainly two aspects. First, the name of Accumulate indicates an algorithm that generates interval statistics, and for_each sounds like you just have to do some of the elements of the interval, and, of course, that is the main application of that algorithm. Statard with for_each is legal, but it doesn't have Accumulate clear. Second, Accumulate directly returns those statistics we want, and for_each returns a function object, we must extract the desired statistics from this object. In C , then we must add a member function to the imitation function class, let us find the statistics we pursue.

This is another example, this time you use for_each instead of Accumulate:

Struct Point {...); //

Class PointAverage:

Public unary_function {// See Terms 40

PUBLIC:

Pointaverage (): Xsum (0), YSUM (0), Numpoints (0) {}

Void Operator () (Const Point & P)

{

Numpoints;

XSUM = P.x;

YSUM = P.y;

}

Point Result () Const

{

Return Point (Xsum / Numpoints, Ysum / Numpoints);

}

Private:

SIZE_T NUMPOINTS;

Double XSUM;

Double Ysum;

}

List IP;

...

Point avg = for_each (lp.begin (), lp.end (), pointAVerage ()). Result;

As far as I personally, I prefer to use Accumulate to count, because I think it is clearly expressed, but for_each can also, and unlike an accumulate, the problem with side effects does not follow for_each. Both algorithms can be used to count. Use the one best for you.

You may want to know why FOR_EACH's function parameters allow for side effects, and Accumulate is not allowed. This is a probe problem that piercing the STL heart. Oh, respected readers, some secrets are always outside our knowledge. Why is there a difference between Accumulate and FOR_EACH? I also hope to hear a convincing explanation.

转载请注明原文地址:https://www.9cbs.com/read-4800.html

New Post(0)