How to get the number of items in a Perl list constant?

105 Views Asked by At

The documentation states that a list constant in scalar context currently returns the number of values, but this may change in the future. It also states that, to use a list constant as an array, place it in parentheses. So, what is the correct way to get the number of values in a list constant, and what am I doing wrong in the code below? I get the error:

Bareword found where operator expected at D:\Batch/list-const-error.pl line 5, near "@(INFOFIELDS"
        (Missing operator before INFOFIELDS?)
syntax error at D:\Batch/list-const-error.pl line 5, near "@(INFOFIELDS"

use strict;
use warnings;
use constant INFOFIELDS  => qw( filedate filetime filesize mtime filename );
use constant OUTTEMPLATE => '   A9       A7       A12      A12   A* ';
use constant NELEMENTS   => scalar @(INFOFIELDS);
my $line = pack( OUTTEMPLATE, @ARGV[0..NELEMENTS-1] );
print( "$line\n" );
4

There are 4 best solutions below

1
Steffen Ullrich On

A constant can be used as a subroutine, so you can call scalar INFOFIELDS() or scalar &INFOFIELDS to access the number of items in the constant list.

3
jhnc On

You don't need the @:

use constant NELEMENTS => scalar (INFOFIELDS);
2
brian d foy On

I know that you are specifically asking about a constant subroutine made with the constant pragma, and other answers have already noted that you don't need to the @ around the return value of a subroutine.

The general way to get the number of elements in a list is to assign the result of a list assignment. Even an assignment to the empty list is good enough:

$ perl -le 'print $c = () = qw(a b c)'
3

$ perl -le '@c = qw(1 2 4 6); print $c = () = @c'  # but also scalar @c
4

$ perl -le 'print $c = () = 1 .. 37'
37

$ perl -le 'print $c = () = localtime'
9

$ perl -le 'use constant L => qw( v 7 ); $c = () = L; print $c'
2

If the thing supplying the list is an array (as it is in constant), scalar gives the right answer:

$ perl -le 'use constant L => qw( v 7 ); $c = scalar  L; print $c'
2

This works because you are asking for scalar context on L, which puts the last evaluated expression in L() in scalar context. The expression is the array variable that constant made to hold your list.

However, the scalar goes wrong in the (non-array) list case, at least for what people blindly expect. This gives the last element of the list:

$ perl -le 'print $c = scalar qw(a b c)'
c

Since many people use qw(1 2 3) as their input list when they try this, they accidentally get 3 and think it worked out. That's why I write so much about that in perlfaq4's What is the difference between a list and an array?.

0
zdim On

Can also get your read-only variables (constants) as plain old lexicals, using another library

use Const::Fast; 

const my @constary => qw(one two); 

Then use it as any other array to get its length

my $num_elems = @constary;                # or

const my $num_elems => scalar @constary;  # better as length is constant

or

say "Number of elements: ", scalar @constary;  # or 0+@constary

to get the number of elements on the fly. Here scalar forces scalar context, in which an array is evaluated to its length (number of elements), and 0+... forces numeric (and scalar) context.

Now there's no puzzle, use these lexical variables normally as any other. See Const::Fast. Its code is 74 lines.