[Dev] STL for_each underwhelms me

Tanner Lovelace dev@trilug.org
11 Feb 2002 15:15:51 -0500


--=-fvvB9qBifs/mBVI3DJ9h
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

On Mon, 2002-02-11 at 13:13, M. Mueller (bhu5nji) wrote:
> (darn laptop and fat fingers...)
>=20
> At first I though the STL "for_each" algorithm was going to be handy.  Th=
en I=20
> tried using it. =20
>=20
> I know very well what a "unary function" is and how they are different fr=
om=20
> "binary functions" and "unary methods".  I searched the web for advice on=
 how=20
> to get around the limitations of for_each only operating in unary functio=
ns. =20
> The workarounds were studies in obuscation.   Or maybe I should look deep=
er? =20
> Am I missing the finer points?  Here's how I understand things right now.
>=20
> Why is the following code structure in need of replacement by such a limi=
ted=20
> algorithm as for_each?
>=20
> The mortal way:
>=20
> map<int,int> xmap;
>=20
> // load map...
>=20
> map<int, int>::iterator i;
> for(i =3D xmap.begin(), i !=3D xmap.end(), i++)
> {
> 	// do any darn thing you want to
> 	// in plain old un-elegant but readable
> 	// code
> }
>=20
> The for_each way:
>=20
> Either:
> for_each(xmap.begin(), xmap.end(), unary_function_only);
> Or:
> for_each(xmap.begin(), xmap.end(), class_with_overloaded_operators);
> Or:
> for_each(xmap.begin(), xmap.end(),=20
> magically_transformed_things_that_appear_to_be_UF);
>=20
> The first way is OK, but it's limited to only unary functions.  I tried t=
o=20
> use a unary method and kept getting "out of scope" errors during compile.=
 =20
> The tranformation techniques I saw on the web were so obtuse that they=20
> probably would not have survived code reviews in places I have worked in =
the=20
> past.
>=20
> Someone, please turn on the light for me.

Perhaps it would help if you would explain a bit more about
what you're trying to do, and why a unary function won't work
for you?  Without knowing that, however, you probably want to
look around for documentation about a "function object" which
is an object designed to look like a function but that can
hold state.=20

Say, for example, you want to take a vector of ints and calculate
it's mean value.  With a plain old for, you could loop through
each element of the vector and calculate the sum and finally=20
divide by the number of elements.  With for_each, however, you
could do it like this:

// function object to process the mean value
class MeanValue {
 private:
    long num; // number of elements
    long sum; // sum of all elements
 public:
    // Constructor
    MeanValue() : num(0), sum(0) {}

    // function call
    void operator() (int elem) {
      ++num; // increment count
      sum +=3D elem;
    }

    // return mean value
    double value () {
      return static_cast<double>(sum) / static_cast<double>(num);
    }
};

int main(void)
{
  vector<int> coll;

  // insert elements from 1 to 8
  for (int i =3D 1; i <=3D8; ++i) {
    coll.push_back(i);
  }

  // Process and print mean value
  MeanValue mv =3D for_each(col.begin(), coll.end(), MeanValue());
 =20
  cout << "mean value: " << mv.value() << endl;

  return 0;
}

Note that this also shows another feature of for_each, it=20
returns the (modified) function object passed to it.  As far
as I know, this is the only STL algorithm that does that.
I believe this is an example of what you called
"class_with_overloaded_operators".

Your "magically_transformed_things_that_appear_to_be_UF" are better
know as "function adapters".  basically, they're functions that
you can pass a second (or first) argument to them and they'll
handle the fact that for_each only expects a unary function.
So, if you wanted to multiply each element by a number, you'd
need a function adapter that took the number you wanted to
multiply by and provided a function for for_each to call.

Also, note that if you're trying to modify a bunch of elements,
transform is often a better function to use.  The difference
between tranform and for_each is that for_each expects a
unary function that returns void, so if you want to modify
the elements, you need to pass them by reference.  Transform,
on the other hand, takes a unary operation that returns=20
a value.  This value replaces the previous value in that
position of the container.  So, if you wanted to square a
bunch of elements, you could do either this:

void square(int &elem) { elem =3D elem * elem; }
for_each(coll.begin(), coll.end() square);

or this:

int square(int elem) { return elem * elem; }
transform(coll.begin(), coll.end(), // source range
          coll.begin(),             // destination range
          square);                  // operation.

But, once again, because C++ offers lots of ways to do things,
it would help if we knew just what it was you're doing. :-)

Tanner
--=20
Tanner Lovelace | lovelace@wayfarer.org | http://wtl.wayfarer.org/
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
GPG Fingerprint =3D A66C 8660 924F 5F8C 71DA  BDD0 CE09 4F8E DE76 39D4
GPG Key can be found at http://wtl.wayfarer.org/lovelace.gpg.asc
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
 Those who are willing to sacrifice essential liberties for a little=20
 order, will lose both and deserve neither.  --  Benjamin Franklin=20

 History teaches that grave threats to liberty often come in times
 of urgency, when constitutional rights seem too extravagant to=20
 endure.  --  Justice Thurgood Marshall, 1989=20

--=-fvvB9qBifs/mBVI3DJ9h
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQA8aCZ2zglPjt52OdQRAs1tAJ90MVaDU4pz1UPmXz20RjC5AYA92ACgky36
0j3CoIMF6CMEDuUBIcCwu2U=
=JG5E
-----END PGP SIGNATURE-----

--=-fvvB9qBifs/mBVI3DJ9h--