Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I find the number of values in a Perl list?

Tags:

list

perl

The number of values in a list can only be determined by iterating over its values, or converting it to an array. Assigning it to a scalar won't return the items count:

my $n = ('a', 'b', 'c');  # $n = 'c' 

There's an "empty parentheses" idiom, that can be used to get the number of elements:

my $n = () = ('a', 'b', 'c'); # $n = 3

Is it equivalent internally to

my $n = @{[ 'a', 'b', 'c' ]};

?

like image 900
Eugene Yarmash Avatar asked Feb 08 '10 22:02

Eugene Yarmash


2 Answers

The two items you showed are not equivalent. But they have the same final result;

my $n = @{[ 'a', 'b', 'c' ]};

Here you create an anonymous array [ 'a', 'b', 'c' ], then dereference it and take the count of members. The creation of the array provides a list context to the comma operators in the statement.

my $n = () = ('a', 'b', 'c');

Here we use the infamous "goatse operator". The list ('a', 'b', 'c') is assigned to an empty list () = ('a', 'b', 'c'); . The result of the list assignment is assigned to $n. List assignment returns the number of items on the right hand side of the assignment in scalar context (in list context, you get the list of values assigned to).

like image 145
daotoad Avatar answered Sep 26 '22 04:09

daotoad


This is an interesting implementation detail: Does the assignment to an empty list create an (unnecessary) anonymous array?

There are two ways of answering this question: First, The Right Way: Try to figure out how this might be handled in the source. Is there a special case for assignment to an empty list evaluated in scalar context?

Being the lazy and ignorant type, I chose to use Benchmark:

#!/usr/bin/perl

use strict; use warnings;
use Benchmark qw( cmpthese );

cmpthese -5,  {
    goatse => sub { my $n = () = 'A' .. 'Z' },
    anon   => sub { my $n = @{[ 'A' .. 'Z' ]}},
};

I ran the benchmark a bunch of times, and the assignment to empty list had a slight advantage in all cases. If the difference were purely random, the probability of observing 10 timings all in favor of goatse is less than 0.1%, so I am assuming there is some kind of short circuit.

On the other hand, as running the benchmark @daotoad posted in the comments, probably gives a more complete picture:

#!/usr/bin/perl

use strict; use warnings;
use Benchmark qw( cmpthese );

use constant START => 1;
use constant STOP => 1000;

my $start = START;
my $stop = STOP;

cmpthese -5, {
    anon => sub { my $n = @{[ $start .. $stop ]}},
    goatse => sub { my $n = () = $start .. $stop },
    canon => sub { my $n = @{[ START .. STOP ]}},
    cgoatse => sub { my $n = () = START .. STOP },
};

Typical results on my machine (Windows XP Pro SP3, Core 2 Duo, 2 Gb memory, ActiveState perl 5.10.1.1006):

           Rate    anon cgoatse  goatse   canon
anon     5207/s      --    -45%    -49%    -51%
cgoatse  9522/s     83%      --     -7%    -10%
goatse  10201/s     96%      7%      --     -4%
canon   10636/s    104%     12%      4%      --

And, with:

use constant START => 'AAAA';
use constant STOP => 'ZZZZ';

the results are:

          Rate    anon  goatse cgoatse   canon
anon    1.73/s      --    -12%    -16%    -17%
goatse  1.98/s     14%      --     -4%     -5%
cgoatse 2.06/s     19%      4%      --     -1%
canon   2.08/s     20%      5%      1%      --

Conclusion:

If in doubt, use my $n = () = ...;

like image 33
Sinan Ünür Avatar answered Sep 26 '22 04:09

Sinan Ünür