in reply to Re: how to avoid nested looping?
in thread how to avoid nested looping?

Side note: map in void context no longer carries the overhead that it did and, in any event, if you must apply the same behavior to every element in a list, many programmers find using map is often clearer than foreach. Sometimes it's just a stylistic thing.

Of course, you point out that the word "map" can be misleading, but if one merely thinks of the word "apply" or something else in place of map, it makes more sense. After all, Lisp programmers have gotten along with lambda for many years. (Not to mention car and cdr!)

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re^3: how to avoid nested looping?
by bgreenlee (Friar) on Feb 04, 2005 at 07:01 UTC

    For me it's more a matter of clarity/readability for someone looking over my code, particularly with nested maps like this (do you really find map { map { ... } @bar } @foo easier to mentally parse than foreach (@foo) { foreach (@bar) { ... } }?). But, you're right...it's mostly a stylistic thing and this is a classic example of TMTOWTDI.

    Update: I tried a little test and foreach was actually significantly faster (for this particular test, that is):

    #!perl -w use strict; use Benchmark; Benchmark::cmpthese(-5, { 'foreach' => sub { my $sum=0; foreach (1..100) { foreach (1..100) +{ $sum++ }}}, 'map' => sub {my $sum=0; map { map { $sum++ } (1..100) } (1..100) +}, });
    ------------------------------ Rate map foreach map 345/s -- -43% foreach 603/s 75% --

    What gives?

    -b

      What version of Perl are you using and on which platform? perl -V would be nice. I'm not saying map is faster, but then, I'm not really worried about it. I'm more worried about code correctness and maintainability.

      One interesting thing about map and friends which is not appreciated enough is how map says "do this for everything in the list" when foreach says "do this to each thing in the list." It's a subtle distinction, but one study of programming discovered that programmers tend to make fewer errors when using commands that operate on an entire set instead of looping constructs that operate on individual members of the set. Unfortunately for most programmers, we're given tools for the latter while the former are often considered "advanced" when in reality their simpler. We get taught the bad way and we get used to it. Go figure :)

      Cheers,
      Ovid

      New address of my CGI Course.

        I tried bgreenlees program and this is what i got.

        This is perl, v5.9.1 built for i686-linux.
        Rate map foreach map 313/s -- -43% foreach 552/s 76% -- Rate map foreach map 313/s -- -44% foreach 558/s 78% --

        This is perl, v5.8.3 built for i686-linux
        Rate map foreach map 308/s -- -47% foreach 581/s 89% -- Rate map foreach map 308/s -- -47% foreach 578/s 88% --

        That was run under 5.8.5. Full -V output:

        -b

      Two things are important here. First is that foreach (N..M) is an optimized construct - map {} N..M isn't. Second is that a foreach() is slightly faster than a map. So, if you do 101 of them, it adds up. Here's a somewhat different test:
      use strict; use warnings; use Benchmark 'cmpthese'; my $nums1 = join(",", 1..100); my $nums2 = "1..100"; cmpthese(-1, { for1 => "my \$sum=0; foreach ($nums1) {\$sum++}", for2 => "my \$sum=0; foreach ($nums2) {\$sum++}", for3 => "my \$sum=0; \$sum++ foreach ($nums1)", for4 => "my \$sum=0; \$sum++ foreach ($nums2)", map1 => "my \$sum=0; map {\$sum++} $nums1", map2 => "my \$sum=0; map {\$sum++} $nums2", map3 => "my \$sum=0; map \$sum++, $nums1", map4 => "my \$sum=0; map \$sum++, $nums2", }); __END__ Rate map1 map3 map2 map4 for1 for3 for2 for4 map1 33185/s -- -3% -5% -5% -9% -17% -25% -34% map3 34296/s 3% -- -1% -2% -6% -15% -22% -32% map2 34795/s 5% 1% -- -0% -5% -13% -21% -31% map4 34909/s 5% 2% 0% -- -5% -13% -21% -31% for1 36571/s 10% 7% 5% 5% -- -9% -17% -27% for3 40193/s 21% 17% 16% 15% 10% -- -9% -20% for2 43997/s 33% 28% 26% 26% 20% 9% -- -12% for4 50243/s 51% 46% 44% 44% 37% 25% 14% --
      Note that the differences between 'map1' and 'map3' and between 'map2' and 'map4' are smaller than the differences between 'for1' and 'for3' and the difference between 'for2' and 'for3'. And that the non-optimized fors ('for1' and 'for3') are somewhat faster than the equivalent maps ('map1' and 'map3'). Now, if you do that 101 times, I'm surprised the difference is only 75%.

      Perhaps the most interesting number in the above table is the 51% in the lower left corner. It means that if you compare the fastest foreach (expression form, optimized range), and pit that against the slowest map (block form, no range), and have a very minimal body, thus giving the loop overhead a significant weight, your gain is still only about 50%.