### counting backward

by ag4ve (Monk)
 on Mar 02, 2013 at 14:36 UTC 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.

Replies are listed 'Best First'.
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<> }
+ "

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.
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

Re: counting backward
by Athanasius (Chancellor) 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%           --
```#! perl
use strict;
use warnings;

use IO::Null;

=pod

=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.

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 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 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.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1021418]
Approved by BrowserUk
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (6)
As of 2018-06-20 13:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Should cpanminus be part of the standard Perl release?

Results (116 votes). Check out past polls.

Notices?