Perl Part I · Module 3 · Operators and Expressions
Range and Repetition Operators
Every operator so far has examined values or combined them. These two make values: the range operator counts from one endpoint to another, and the repetition operator you met on strings has a second form that repeats a whole list. Both are small and both have one behaviour that catches people. A range whose first endpoint is larger than its second does not count downwards — it produces nothing at all, which turns a reversed pair of variables into a loop that silently never runs. And whether x repeats a list or a string is decided by a pair of parentheses that look like punctuation rather than meaning. Neither case warns.
Foundation12 min readPerlPart IFundamentalsListsOperators
Part I · Module 3 · Chapter 3.10 · Range and Repetition Operators
1. .. Builds a Sequence
use strict;
use warnings;
print "1 .. 5 : ", join(" ", 1 .. 5), "\n";
print "3 .. 3 : ", join(" ", 3 .. 3), "\n";
print "'a' .. 'e': ", join(" ", "a" .. "e"), "\n";1 .. 5 : 1 2 3 4 5
3 .. 3 : 3
'a' .. 'e': a b c d eThe range operator produces every value from the left endpoint to the right one, inclusive at both ends. 1 .. 5 is five values, not four. Equal endpoints give a one-element list.
Its most common use is driving a loop over a fixed count — for my $i (1 .. $cycles) — which is Module 9's subject. Here the operator is the point.
2. A Backwards Range Is Empty, Not Reversed
use strict;
use warnings;
my @down = (5 .. 1);
print "5 .. 1 has ", scalar(@down), " elements: [", join(" ", @down), "]\n";5 .. 1 has 0 elements: []5 .. 1 is the empty list. It does not count down, and it is not an error.
That matters because of what it does to a loop. for my $i ($start .. $end) with the two variables the wrong way round does not fail, does not warn, and does not iterate — the loop body simply never executes. A script built on that reports zero of whatever it was counting, cleanly and with exit status zero. This is the same failure shape as Chapter 3.5's filter: the wrong answer arrives looking like a legitimate result.
To count downwards you reverse the list explicitly: reverse 1 .. 5 gives 5 4 3 2 1. reverse is Module 5's.
3. Ranges on Letters
The right-hand endpoint does not have to be a number:
use strict;
use warnings;
print "'aa' .. 'ad': ", join(" ", "aa" .. "ad"), "\n";
print "'Az' .. 'Bb': ", join(" ", "Az" .. "Bb"), "\n";
print "'ay' .. 'bb': ", join(" ", "ay" .. "bb"), "\n";'aa' .. 'ad': aa ab ac ad
'Az' .. 'Bb': Az Ba Bb
'ay' .. 'bb': ay az ba bbOn strings, .. uses Perl's magic string increment, which advances the last character and carries into the one before it when it wraps — so az is followed by ba, and Az by Ba. It is the same rule that makes spreadsheet column names work.
This is genuinely useful for generating fixed label sets — bank names, lane identifiers, port suffixes. It is also easy to misjudge: the carry depends on both endpoints having a form the increment can walk between, and a range like "a9" .. "b2" is not something to guess at. Generate it once, look at the output, and then rely on it.
Note too that a numeric-looking endpoint is treated as a number. 1.5 .. 4.5 gives 1 2 3 4 — both endpoints are truncated to integers before the range is built, which is the same truncate-toward-zero behaviour as int() in Chapter 3.2 §2.
4. x on a List
Chapter 3.3 showed x repeating a string. With a list on its left it repeats the list:
use strict;
use warnings;
my @zeros = (0) x 5;
my @pairs = (1, 2) x 3;
my @none = (7) x 0;
print "(0) x 5 : ", scalar(@zeros), " elements: ", join(" ", @zeros), "\n";
print "(1,2) x 3 : ", scalar(@pairs), " elements: ", join(" ", @pairs), "\n";
print "(7) x 0 : ", scalar(@none), " elements: [", join(" ", @none), "]\n";(0) x 5 : 5 elements: 0 0 0 0 0
(1,2) x 3 : 6 elements: 1 2 1 2 1 2
(7) x 0 : 0 elements: [](0) x 5 is the normal way to build an initialised list of a known length — a zeroed set of per-lane counters, for instance. (1, 2) x 3 repeats the whole list, giving six elements rather than three.
5. Two Parentheses Decide Which x You Get
This is the sharp edge:
use strict;
use warnings;
my @listwise = ("ab") x 3;
my @stringwise = "ab" x 3;
print "(\"ab\") x 3 -> ", scalar(@listwise), " element(s): ", join(" | ", @listwise), "\n";
print "\"ab\" x 3 -> ", scalar(@stringwise), " element(s): ", join(" | ", @stringwise), "\n";("ab") x 3 -> 3 element(s): ab | ab | ab
"ab" x 3 -> 1 element(s): abababThe same operator, the same operands, the same assignment target — and two different results, differing only by a pair of parentheses.
The rule: x repeats a list when its left operand is a parenthesised list, and a string otherwise. It does not matter that the result is being assigned to an array. "ab" x 3 is string repetition first, producing ababab, and the array then receives that one string.
6. In Scalar Context, .. Is a Different Operator Entirely
This is worth meeting once so that the warning is recognisable when you hit it:
use strict;
use warnings;
my $what = (1 .. 5);
print "got: [", (defined $what ? $what : "undef"), "]\n";got: []Use of uninitialized value $. in range (or flip) at scalar-range.pl line 4.You might expect (1 .. 5) in scalar context to give 5, or the count, or the last element. It gives none of those, and the warning explains why: in scalar context .. is the flip-flop operator, a completely different thing that is designed for selecting a span of lines from an input stream, and which reads the line-number variable $..
So the result is not a range that got squashed into a scalar. A different operator ran.
That is the clearest demonstration in the track of the point Chapter 2.9 made: context is not a conversion applied to a finished value. The surroundings are part of what the expression means, and here they decide which of two operators the symbol .. refers to. Chapter 2.10's advice to state your context explicitly applies with unusual force.
The flip-flop operator itself is a specialist tool for line-oriented input; $. belongs to Module 13. What you need from this section is to recognise in range (or flip) in a warning and know that it means a range landed somewhere that wanted one value.
7. Why This Matters in Engineering Work
The reversed range is a silent no-op. for my $i ($first_fail .. $last_fail) with the endpoints computed from parsed data will, on the run where they come out the wrong way round, do nothing at all and report nothing at all. The guard is to check that the endpoints are ordered before using them, which is one comparison — and Chapter 3.6 is the chapter about choosing the right one.
(0) x $n is how you build a fixed-width accumulator. Per-lane error counts, per-corner slack buckets, a histogram with a known number of bins. Getting the parentheses wrong gives you one element and a script that then indexes past the end of it.
Letter ranges are good for labels and bad for guessing. "a" .. "h" for eight lanes is clear and correct. Anything less obvious should be printed once and inspected rather than reasoned about, because magic string increment has carry rules you will not reconstruct correctly from memory.
8. Common Mistakes
Expecting 5 .. 1 to count down. It is the empty list. Use reverse 1 .. 5.
Omitting the parentheses in list repetition. $v x $n repeats the string; ($v) x $n repeats the list.
Expecting .. in scalar context to give a count or a last element. It is the flip-flop operator, a different operator, and it will warn about $..
Assuming a range excludes its upper endpoint. Both ends are included: 1 .. 5 is five values.
Guessing at a letter range's contents. Generate it and look.
Forgetting that a float endpoint is truncated. 1.5 .. 4.5 is 1 2 3 4.
9. Exercise
Exercise 1 — the endpoints. Print 1 .. 5, 3 .. 3 and 5 .. 1, reporting the element count for each. Then say in one sentence what a loop over the third one does.
Exercise 2 — count down. Produce the list 5 4 3 2 1 using a range. State which function you needed and why the range alone cannot do it.
Exercise 3 — the parentheses. Build two arrays, one with ("-") x 10 and one with "-" x 10, and print the element count and the contents of each. Then say which one you would use to build a list of ten separator cells, and which to build a ten-character rule.
Exercise 4 — a zeroed accumulator. Using (0) x $lanes with my $lanes = 8;, build a list of per-lane counters and confirm it has eight elements. Then deliberately drop the parentheses and record what the element count becomes.
Exercise 5 — letters. Generate "a" .. "h" and confirm it gives eight labels. Then generate "ax" .. "bc" and record the result exactly, rather than predicting it.
Exercise 6 — the scalar-context warning. Run the §6 script and capture STDERR. Quote the warning, name the operator it mentions, and write one sentence explaining why the result was not 5.
10. Summary
..builds an inclusive sequence from its left endpoint to its right one.1 .. 5is five values.- A backwards range is the empty list, not a countdown — so a loop over it runs zero times, silently.
reverseis the fix. - On strings,
..uses magic string increment and carries, soazis followed byba. Generate unfamiliar ranges and inspect them. - Float endpoints are truncated before the range is built.
xrepeats a list when its left operand is a parenthesised list, and a string otherwise.("ab") x 3is three elements;"ab" x 3is one.- Those parentheses are meaning, not punctuation, and nothing warns if you omit them.
- In scalar context
..is the flip-flop operator — a different operator, which warns about$.. Context decides which operator the symbol means, not merely how its result is read.
Chapter 3.11 covers the bitwise operators, where Perl's behaviour differs from a hardware engineer's expectation in two specific and measurable ways.
Where this fits
Part of the Perl curriculum.
