# 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: ```perl 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 [`++`](precedence.md) is used, which gives you the natural alphabetic spread (and on `'aa'` through `'zz'` the expected 676 two-letter combinations). A common Perl idiom: ```perl 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`: ```perl 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: ```perl '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: ```perl 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. ```perl while (<$fh>) { if (/^BEGIN/ .. /^END/) { # true from BEGIN line through END line print; # print everything in between, inclusive } } ``` The semantics: 1. The flip-flop is initially **false**. 2. When the **left** condition (`/^BEGIN/`) becomes true, the flip-flop transitions to **true**, and the right side is evaluated immediately on the same iteration. 3. 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. 4. 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. ```perl while (<$fh>) { print if // .. /<\/head>/; # one-line ... works print if // ... /<\/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: ```perl # 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`](../perlfunc/reverse.md), [`grep`](../perlfunc/grep.md), [`map`](../perlfunc/map.md) — list operators commonly composed with ranges. - [Subscript](subscript.md) — array slices `@arr[1..3]` rely on range-in-list-context. - [Precedence](precedence.md) — `..` and `...` sit at row 17, between `||`/`//` and `?:`. Almost always parenthesise when combining with logic.