Range and flip-flop operators#
Two operators with two completely different jobs depending on context: in list context they generate ranges, and in scalar context they are stateful ”flip-flop“ boolean expressions for matching line ranges in stream-processing.
Operator | List-context job | Scalar-context job |
|---|---|---|
| inclusive range | flip-flop (false-tested entry) |
| inclusive range | flip-flop (true-tested entry) |
.. in list context — ranges#
In list context, .. produces the inclusive sequence between its endpoints:
0..9 # (0,1,2,3,4,5,6,7,8,9)
'a'..'z' # ('a','b',...,'y','z') 26 elements
'aa'..'zz' # ('aa','ab',...,'zy','zz') 676 elements
'A'..'F' # ('A','B','C','D','E','F')
for my $i (1..100) { ... } # numeric for-loop
for my $name ('alpha'..'omega') { ... } # alphabetic, magic-incremented
For numeric endpoints the result is the integer sequence (with strict ordering — 5..1 is the empty list, never reverse). For string endpoints the magic-increment from ++ is used, which gives you the natural alphabetic spread (and on 'aa' through 'zz' the expected 676 two-letter combinations).
A common Perl idiom:
my @hex = ('0'..'9', 'a'..'f'); # 16 hex digits as strings
my @rev = reverse 1..10; # (10,9,8,...,1)
@arr[ 5..9 ] # array slice, indexes 5..9
@arr[ -3..-1 ] # last three elements
The all-integer range can be very large — 1..1_000_000_000 is a billion-element list. Perl 5 lazily generates loop ranges in for, so for my $i (1..1_000_000_000) is safe, but my @x = 1..1_000_000_000 allocates the whole array. PetaPerl follows the same rule.
Reversed endpoints#
5..1 is empty. There is no ”reverse range“ form; if you want descending, generate ascending and reverse:
1..5 # (1,2,3,4,5)
5..1 # () — empty, no warning
reverse 1..5 # (5,4,3,2,1)
Mixed-type endpoints#
If either endpoint looks numeric, .. does numeric ranging:
'1'..'5' # (1,2,3,4,5) — numeric
'1'..'10' # (1,2,3,4,5,6,7,8,9,10) — numeric
'a'..'5' # ('a') — magic-increment from "a",
# stops because it can't reach "5"
Make the type explicit to avoid confusion:
0+'1' .. 0+'5' # numeric, no doubt
sprintf("%d", 1) .. sprintf("%d", 5)
In practice, write integer ranges with bare integer literals and character ranges with bare quoted-string letters; the magic-vs- arithmetic distinction follows automatically.
.. and ... in scalar context — flip-flops#
Inside a boolean context (an if, a while, a ?:, the right of &&/||///), .. becomes a stateful expression that remembers whether it is currently between a left-side match and a right-side match.
while (<$fh>) {
if (/^BEGIN/ .. /^END/) { # true from BEGIN line through END line
print; # print everything in between, inclusive
}
}
The semantics:
The flip-flop is initially false.
When the left condition (
/^BEGIN/) becomes true, the flip-flop transitions to true, and the right side is evaluated immediately on the same iteration.The flip-flop stays true on subsequent iterations until the right condition (
/^END/) becomes true. On that iteration it returns true and then transitions back to false.So both the BEGIN line and the END line are included.
When the flip-flop is true, it returns a sequence number (1 on the entering iteration, 2 on the next, …). On the closing iteration it returns the count followed by E0 — so the value is true (non-empty string, not the false "0") but stringifies as the number 0 for cosmetic clarity. You can usually treat it as plain boolean.
... differs from .. in one detail:
..(two dots) — when the left side becomes true, the right side is checked on the same iteration. So the same line can open and close the range....(three dots) — when the left side becomes true, the right side is not checked until the next iteration. Always a multi-line range.
while (<$fh>) {
print if /<head>/ .. /<\/head>/; # one-line <head>...</head> works
print if /<head>/ ... /<\/head>/; # forces multi-line; same-line ignored
}
When you’d use a flip-flop#
The flip-flop shines in awk-style line-range filtering — pulling sections out of a log file, isolating a fenced code block from markdown, processing a configuration block delimited by keywords. It is famously un-loved by code reviewers because its statefulness is implicit.
Two practical alternatives that some prefer:
# Explicit state variable — equivalent semantics, easier to grep for:
my $in_block = 0;
while (<$fh>) {
$in_block = 1 if /^BEGIN/;
print if $in_block;
$in_block = 0 if /^END/;
}
# A simple two-step iterator:
while (<$fh>) {
if (/^BEGIN/ .. /^END/) { print }
}
The flip-flop wins on conciseness; the explicit form wins on clarity at the cost of three more lines. Pick by audience.
See also#
reverse,grep,map— list operators commonly composed with ranges.Subscript — array slices
@arr[1..3]rely on range-in-list-context.Precedence —
..and...sit at row 17, between||///and?:. Almost always parenthesise when combining with logic.