Skip to content
VLSI Mentor

Perl Part I · Module 5 · Arrays and Lists

Arrays in Scalar Context

Chapter 5.6 established that an array asked for a single value reports how many elements it holds. This chapter turns that into the most useful idiom in the module. Because the count is what you get, and because zero is the only count that is false, putting an array directly into a condition asks exactly the question you usually want — did anything end up in here — with no comparison, no length function and no chance of an off-by-one. The work is in knowing which surroundings ask for one value, because the answer is not obvious: interpolating an array into a string gives you its elements while concatenating one gives you its count, and those two lines look nearly identical.

Foundation14 min readPerlPart IFundamentalsArraysContextIdioms

Part I · Module 5 · Chapter 5.7 · Arrays in Scalar Context

1. The Count, for Free

Azvya Education Pvt. Ltd.VLSI Mentor
the-count.pl
use strict;
use warnings;

my @failures = ("fifo", "regfile");

printf "scalar()        : %d\n", scalar(@failures);
printf "scalar assign   : %d\n", do { my $n = @failures; $n };
printf "numeric compare : %s\n", (@failures > 1 ? "more than one" : "one or none");
printf "arithmetic      : %d\n", @failures * 10;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
scalar()        : 2
scalar assign   : 2
numeric compare : more than one
arithmetic      : 20

Four different surroundings, all asking for one value, all getting 2.

  • scalar(@a) asks explicitly — Chapter 2.10's operator.
  • my $n = @a is a scalar assignment, so the right-hand side is asked for one value.
  • @a > 1 is a numeric comparison, and Chapter 3.5 established that > wants numbers.
  • @a * 10 is arithmetic, which also wants a number.

The do { ... } in the second line is only there to fit a declaration into a printf argument; ignore it as scaffolding.

2. Which Surroundings Ask for One Value

This is the part worth having as a reference, because guessing produces Chapter 2.9's class of bug.

SurroundingContext@a with 3 elements gives
scalar(@a)scalar3
my $n = @a;scalar3
if (@a), unless (@a), while (@a)scalar3, which is true
@a == 0, @a > 2scalar3
@a + 1scalar4
"text " . @ascalartext 3
"@a" in a stringlista b c
print @alistthe elements
my ($x) = @a;list$x is the first element
push @b, @a;listthree elements appended
sort @a, grep ... @a, join ",", @alistthe elements

The pattern behind the table: anything that wants a single value imposes scalar context; anything that wants several imposes list context. That is Chapter 2.9's rule, and the table is just that rule applied to the surroundings you will actually meet.

The pair that proves it

Two string operations on the same array, one line apart:

Azvya Education Pvt. Ltd.VLSI Mentor
interp-vs-concat.pl
use strict;
use warnings;

my @failures = ("fifo", "regfile");

print "interpolated : @failures\n";
print "concatenated : " . @failures . "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
interpolated : fifo regfile
concatenated : 2

The same array. Two string operations. The elements, then the count.

  • Interpolation puts the array's elements into the string, space-separated — because a string is being built from several values.
  • Concatenation with . wants one value on each side, by Chapter 3.3 §1, so the array gives its count.

Nothing warns about either. Both are correct for what was written, and the difference between them is whether the array is inside the quotes or outside them.

3. The Idiom: if (@failures)

Azvya Education Pvt. Ltd.VLSI Mentor
the-idiom.pl
use strict;
use warnings;

my @failures = ("fifo", "regfile");
my @clean    = ();

if (@failures) {
    printf "%d test(s) failed: %s\n", scalar(@failures), join(", ", @failures);
}

unless (@clean) {
    print "clean run — nothing to report\n";
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
2 test(s) failed: fifo, regfile
clean run — nothing to report

if (@failures) reads as "if anything failed", and it is exactly that. The condition imposes scalar context, the array gives its count, and the only count that is false is 0 — by Chapter 2.8.

Compare the alternatives:

Written asWorks?Comment
if (@failures)yesidiomatic; reads as the question
if (scalar(@failures))yesexplicit; redundant here but harmless
if (scalar(@failures) > 0)yescorrect and verbose
if ($#failures >= 0)yescorrect and easy to get wrong
if ($#failures)nowrong — see below

That last row is a real bug. $#failures is -1 for an empty array and 0 for a one-element array, by Chapter 5.2 §3. Since -1 is true and 0 is false, if ($#failures) reports true for empty and false for exactly one element — wrong in both directions, which is the same double failure index produced in Chapter 4.5 §2.

Use if (@array). It is shorter and it is right.

4. An Array Holding One False Value Is True

This is the property that makes the idiom safe, and it surprises people.

Azvya Education Pvt. Ltd.VLSI Mentor
truthiness.pl
use strict;
use warnings;

my @empty    = ();
my @one_zero = (0);
my @one_blank = ("");

printf "empty       : %s\n", (@empty     ? "true" : "false");
printf "holds (0)   : %s\n", (@one_zero  ? "true" : "false");
printf "holds (\"\")  : %s\n", (@one_blank ? "true" : "false");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
empty       : false
holds (0)   : true
holds ("")  : true

Only the empty array is false. An array holding a single 0 is true, and so is one holding a single empty string.

The reason is the whole point of the chapter: if (@a) tests the count. For both of those arrays the count is 1, and 1 is true. The contents were never consulted — which is worth stating plainly, because 0 and "" are precisely the values Chapter 2.8 listed as false, and if the idiom tested contents these two rows would read false.

That is the behaviour you want. A list of measured values containing a single 0 is not an empty list — something was measured and the answer was zero. if (@readings) correctly says "there are readings"; the value of those readings is a separate question.

Contrast this with the trap from Chapter 5.4 §3, where while (my $job = shift @work) did test the content and stopped early on a 0. Testing the array tests the count; testing an element tests the element. Those are different questions and the array form is almost always the one you want.

5. print Is List Context

Azvya Education Pvt. Ltd.VLSI Mentor
print-context.pl
use strict;
use warnings;

my @a = ("x", "y", "z");

print "print \@a         -> ";
print @a;
print "\n";

print "print scalar(\@a) -> ", scalar(@a), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
print @a         -> xyz
print scalar(@a) -> 3

print @a wrote the elements, with no separator between them — xyz, not x y z. That is list context: print takes a list of things to output.

Two details worth separating:

  • The elements, not the count. Chapter 2.10 §6 listed "omitting scalar() inside print" as a mistake, and this is it with the array properly introduced.
  • No separator. Interpolation inside a string — print "@a" — joins with spaces. print @a with the array outside the quotes joins with nothing. The space in the interpolated form comes from a special variable that Module 13 covers; the practical rule is to use join when the separator matters.
Azvya Education Pvt. Ltd.VLSI Mentor
three-ways.pl
use strict;
use warnings;

my @a = ("x", "y", "z");

print @a, "\n";
print "@a\n";
print join(", ", @a), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
xyz
x y z
x, y, z

Three forms, three outputs. The third is the one to write when the output matters, because it says what it does.

6. Why This Matters in Engineering Work

if (@failures) is the shape of almost every reporting decision. Did anything fail, was anything skipped, were there violations. One expression, no comparison, nothing to get wrong — and it pairs with the Chapter 5.4 §5 accumulator that filled the array.

while (@work) is the right queue loop for the reason §4 gives: it stops when the queue is empty rather than when an element happens to be false. That is the fix Chapter 5.4 §3 pointed forward to.

The concatenation hazard is a real source of wrong report lines. "found " . @matches . " matches" prints a number where a reader might reasonably expect names, or — worse — prints a count in a sentence written as though it listed items. join and scalar remove the ambiguity, and in a line that someone will act on, removing ambiguity is worth four characters.

And if ($#array) is worth recognising as a bug when you inherit it, because it is subtly wrong rather than obviously broken: it behaves correctly for every array of two or more elements, so it survives testing on realistic data and fails on the empty and single-element cases — which are exactly the boundary cases a clean run and a one-failure run produce.

7. Common Mistakes

Using . where you meant interpolation. "blocks: " . @names gives the count.

Using if ($#array). True when empty, false with one element. Use if (@array).

Expecting if (@a) to test the contents. It tests the count, so (0) is true. That is the desired behaviour.

Expecting print @a to insert separators. It does not; "@a" inserts spaces and join inserts what you ask for.

Writing if (scalar(@a) > 0). Correct but verbose. if (@a) says the same thing.

Assuming an array in a string always gives elements. Inside the quotes, yes. Outside and concatenated, no.

8. Exercise

Exercise 1 — four scalar contexts. With a four-element array, get the count via scalar(), a scalar assignment, a numeric comparison and arithmetic. Confirm all four agree.

Exercise 2 — the proving pair. Print the same array once interpolated inside a string and once concatenated with .. Record both outputs and say which gave the count.

Exercise 3 — the idiom. Write a script with a populated @failures and an empty @clean that reports the failures and confirms the clean run, using if and unless with no comparison operators at all.

Exercise 4 — break it with $#. Test an empty array and a one-element array with if ($#a). Record both results, state which is wrong and why, and name the chapter where index produced the same double failure.

Exercise 5 — one false element. Build three arrays: empty, holding 0, and holding "". Print whether each is true. Then explain in one sentence why two of them are true, and why that is the behaviour you want for a list of measurements.

Exercise 6 — three print forms. Print a three-element array with print @a, print "@a" and print join(", ", @a). Record all three and say which you would use in a report a colleague reads.

Exercise 7 — fix a report line. The line print "skipped: " . @skipped . " of " . @total . "\n"; is meant to list the skipped tests. Say what it actually prints, then rewrite it so it lists the names and reports the counts correctly.

9. Summary

  • An array in scalar context gives its element count. No function call needed.
  • Scalar context comes from: scalar(), a scalar assignment, if/unless/while conditions, numeric comparisons, arithmetic and concatenation with ..
  • List context comes from: interpolation inside a string, print's argument list, a parenthesised left-hand side, push, sort, grep, join.
  • Interpolating gives the elements; concatenating gives the count. Same array, one line apart, neither warns.
  • if (@failures) is the idiom — it asks "is there anything here" and the only false count is 0.
  • if ($#array) is a bug: true when empty, false with one element.
  • An array holding a single 0 is true, because the count is 1. Testing the array is not testing its contents.
  • print @a writes the elements with no separator. Use join when the separator matters.

Chapter 5.8 applies the whole module so far to the job it exists for: turning a line of a data file into an array of fields, and handling the line that has fewer fields than you expected.

Where this fits

Part of the Perl curriculum.