Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

counting backward

by ag4ve (Monk)
on Mar 02, 2013 at 14:36 UTC ( #1021418=perlquestion: print w/ replies, xml ) Need Help??
ag4ve has asked for the wisdom of the Perl Monks concerning the following question:

I'm guessing this is a p5p question, but I figured this might be a better forum.

Why doesn't perl count backward? I've googled and see the obvious solutions to this, but not explanation as to why?
% perl -e 'foreach my $i (10 .. 0) { print "$i ";} print "\n";' % perl -e 'foreach my $i (0 .. 10) { print "$i ";} print "\n";' 0 1 2 3 4 5 6 7 8 9 10

It just seems perl is totally stupid here - first, for not doing what I want and then (even with warnings), it says nothing.

Comment on counting backward
Download Code
Re: counting backward
by BrowserUk (Pope) on Mar 02, 2013 at 14:46 UTC

    Because the range operator is defined as low .. high and as 10 is higher than 0, it does nothing.

    There are a couple of ways of doing what you want:

    • for my $i ( reverse 0 .. 10 ) { print $i }

      Which is okay for small ranges; but creates a huge list for large ones.

    • for my $i ( -10 .. 0 ) { print -$i }

      Which works for any size range without creating a huge list.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Which is okay for small ranges; but creates a huge list for large ones.

      :) seems like an easy candidate for optimization , since this is optimized for (reverse @a) is optimized

      perl -le " @f = 1 .. 100_000; for(reverse @f ){ $f=$_; $f==$#f and sca +lar<> } " perl -le " @f = 1 .. 100_000; for( @f ){ $f=$_; $f==$#f and scalar<> } + "
        AFAIK for( @f ) is optimized to act like an iterator without expanding and caching the list.

        But in for(reverse @f ) there is no for-context which helps reverse to act differently.

        I'm to lazy for benchmarks (we had them before), but this was surely not fixed in 5.8.8

        Cheers Rolf

        But you still had to create a huge array in order to benefit from the optimisation; which kinda negates the purpose.

        This: perl -E"@a=1..1e9; for( reverse @a ) { print $_; <> }" consumes 18GB (that's Gigabytes!) of memory

        Whereas this: perl -E"for( -1e9..1 ) { print -$_; <> }" iterates the same range and consumes only 1.8MB of ram.

        Four orders of magnitude more memory, or a minus sign. My choice is simple; how about yours?


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: counting backward
by jms53 (Monk) on Mar 02, 2013 at 14:51 UTC
    perl -e 'print "$_ " for (reverse (0..10))'
    should work, although not necesarily the most intuitive solution.
    J -
      Heh, I wasn't even asking about the solutions - more about why an inline reverse operator wasn't part of the implementation (somewhat answered quoting the doc) and why this didn't present a warning.

      That said, I love the conversation and seeing foreach (-$var .. 0) { -$_ } - the memory use of reverse and the simplicity of the solution made me say 'damn'.
Re: counting backward
by Athanasius (Monsignor) on Mar 02, 2013 at 16:44 UTC

    I’m a little surprised no-one has suggested a good, old-fashioned, C-style for loop. If speed is a factor, it’s a clear winner over foreach-with-negation:

    #! perl use strict; use warnings; use Benchmark qw( cmpthese ); my $n = 1e4; cmpthese ( 100, { foreach_loop => \&foreach_loop, c_for_loop => \&c_for_loop, } ); sub foreach_loop { for my $i (-$n .. 0) { print -$i; } } sub c_for_loop { for (my $j = $n; $j >= 0; --$j) { print $j; } }

    Output (end only):

    Rate foreach_loop c_for_loop foreach_loop 89.0/s -- -69% c_for_loop 292/s 227% -- 2:32 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      It seems that your output and the output of BrowserUk brings the different results ( Re^5: counting backward (optimize foreach reverse low .. high ).

      I noticed that you print in the loop and BrowserUk calls '1'. I tried to run the both versions and found that it brings the different results indeed:

      Loops with print (I used a NULL device to keep it clear, s.below): Rate foreach_loop c_for_loop foreach_loop 256/s -- -8% c_for_loop 278/s 8% -- Loops with '1': Rate c_for_loop foreach_loop c_for_loop 1314/s -- -22% foreach_loop 1674/s 27% --
      The code (your code :-):
      #! perl use strict; use warnings; use IO::Null; =pod PM Thread perlquestion [id://1021418] =cut use Benchmark qw( cmpthese ); my $n = 1e4; my $null = IO::Null->new; cmpthese ( 10000, { foreach_loop => \&foreach_loop, c_for_loop => \&c_for_loop, } ); sub foreach_loop { for my $i (-$n .. 0) { # $null->print( -$i ); 1; } } sub c_for_loop { for (my $j = $n; $j >= 0; --$j) { # $null->print( $j ); 1; } }

        You caught that I use 1; as the body of the loop to avoid confusing things with the efficiency of the system null device driver (not particularly efficient on Win32). But you missed the point I was trying to make; namely that you cannot use the C-style for as a modifier; which means you must always pay the penalty of creating a new scope for each iteration of the loop.

        Add that to the benchmark to see that cost:

        #! perl use strict; use warnings; #use IO::Null; use Benchmark qw( cmpthese ); my $n = 1e4; #my $null = IO::Null->new; cmpthese 10000, { foreach_loop => \&foreach_loop, foreach_modifier => \&foreach_modifier, c_for_loop => \&c_for_loop, }; sub foreach_loop { for my $i (-$n .. 0) { # $null->print( -$i ); 1; } } sub foreach_modifier { 1 for -$n .. 0; } sub c_for_loop { for (my $j = $n; $j >= 0; --$j) { # $null->print( $j ); 1; } } __END__ C:\test>junk Rate c_for_loop foreach_loop foreach_modi +fier c_for_loop 922/s -- -41% +-52% foreach_loop 1573/s 71% -- +-18% foreach_modifier 1922/s 108% 22% + --

        Of course, some people eschew the use of modifier forms; but then they are probably the same people that favor their own one-off developer time over the every-user, every-time runtime costs.

        As for much of my work I am both user and developer, I don't have that luxury.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        vagabonding electron,

        You are correct that a foreach loop is faster than a C-style for loop, other things being equal. As BrowserUk has explained, this is because with the latter, but not the former,

        you must always pay the penalty of creating a new scope for each iteration of the loop.

        But my point was that if you count upwards from -$n, you add the overhead of a unary negation operator when you come to actually use the original $n:

        #! perl use strict; use warnings; use Benchmark qw( cmpthese ); my $n = 1e6; my $foreach_loop_total = 0; my $c_for_loop_total = 0; cmpthese ( 1000, { foreach_loop => \&foreach_loop, c_for_loop => \&c_for_loop, } ); print "\$foreach_loop_total = $foreach_loop_total\n"; print "\$c_for_loop_total = $c_for_loop_total\n"; sub foreach_loop { $foreach_loop_total += -$_ for -$n .. 0; } sub c_for_loop { for (my $j = $n; $j >= 0; --$j) { $c_for_loop_total += $j; } }

        Output:

        12:00 >perl 555_SoPW.pl Rate foreach_loop c_for_loop foreach_loop 5.62/s -- -8% c_for_loop 6.13/s 9% -- $foreach_loop_total = 500000500000000 $c_for_loop_total = 500000500000000 12:05 >

        So, it appears that the overhead of the additional negation outweighs the benefit of not having to create a new scope on each iteration.

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: counting backward
by tobyink (Abbot) on Mar 03, 2013 at 18:55 UTC

    Have you tried the magic descendi rocket? (-->)

    use strict; use warnings; foreach (my $i = 11; $i --> 0;) { print "$i "; } print "\n";
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

      Cute! But ...

      It forces the use of post increment which is more costly that pre increment. And there is no need to explicitly compare to 0.

      Avoiding those and it comes out quicker than the block form of foreach:

      #! perl use strict; use warnings; #use IO::Null; use Benchmark qw( cmpthese ); my $n = 1e4; #my $null = IO::Null->new; cmpthese 10000, { foreach_loop => \&foreach_loop, foreach_modifier => \&foreach_modifier, c_for_loop => \&c_for_loop, c_for_loop2 => \&c_for_loop2, c_for_loop3 => \&c_for_loop3, c_for_loop4 => \&c_for_loop4, }; sub foreach_loop { for my $i (-$n .. 0) { # $null->print( -$i ); 1; } } sub foreach_modifier { 1 for -$n .. 0; } sub c_for_loop { for (my $j = $n; $j >= 0; --$j) { # $null->print( $j ); 1; } } sub c_for_loop2 { for (my $j = $n; $j-- > 0; ) { # $null->print( $j ); 1; } } sub c_for_loop3 { for (my $j = $n; --$j > 0; ) { # $null->print( $j ); 1; } } sub c_for_loop4 { for (my $j = $n; --$j; ) { # $null->print( $j ); 1; } } __END__ C:\test>junk Rate c_for_loop2 c_for_loop c_for_loop3 foreach_loo +p c_for_loop4 foreach_modifier c_for_loop2 807/s -- -13% -38% -50 +% -53% -56% c_for_loop 926/s 15% -- -29% -43 +% -46% -50% c_for_loop3 1298/s 61% 40% -- -19 +% -24% -29% foreach_loop 1612/s 100% 74% 24% - +- -6% -12% c_for_loop4 1707/s 111% 84% 31% 6 +% -- -7% foreach_modifier 1839/s 128% 99% 42% 14 +% 8% --

      But still the modifier form wins.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1021418]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (13)
As of 2014-07-28 13:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (199 votes), past polls