Skip to content

/g in list and scalar context

Level: 201 · you loop over matches, count them, or reuse a /g match in an if

One line: In list context $s =~ /re/g returns every match at once; in scalar context it returns true or false for the next match and moves pos($s) past it — so while walks the string, my $n = $s =~ /re/g is 1 and not a count, an if (/re/g) searches from where the last one stopped, and a match written inside a function's argument list is in list context.

Perl decides what an expression returns from where it is used. For most operators that is a detail. For m//g it is two different operators with one spelling.

Measured

Verified output of match_g_pl.pl — regenerated by tools/run_examples.py, never hand-typed.

--- list context: every match at once ---
  one group:         200 302 404
  two groups:        GET /home POST /login GET /missing
  no groups:         /home /login /missing
  $n = () = ...:     2

--- scalar context: one match per call ---
  $n = ...:          1   (true, not a count -- and pos($log) is now 3)
  while:             GET /home, pos($log) is now 9
  while:             POST /login, pos($log) is now 26
  while:             GET /missing, pos($log) is now 44
  after the loop:    pos($log) is undef, reset by the match that failed

--- if (/.../g) starts where the last match stopped ---
  found POST, and pos($log) is now 19
  searched from pos 19: no /home, and pos($log) is reset to undef
  asked again, from the start: found /home

--- list context starts from pos() too ---
  pos($log) is 9, left there by the last if
  $log =~ /GET \S+/g returns:  GET /missing
  after pos($log) = undef:     GET /home GET /missing

--- a match in an argument list is in list context ---
  count_args($log =~ /GET/g)   arguments: 2
  count_args($log =~ /GET/)    arguments: 1
  count_args($log =~ /PUT/)    arguments: 0

--- while without /g never moves ---
  stopped by a guard after 1000 turns

List context: all at once

Assigned to an array, a /g match returns the groups of every match in order — one group gives the statuses, two groups give method and path flattened into one list, and no groups give the whole matches. my $n = () = $s =~ /re/g counts matches: the empty list () puts the match in list context, and a list assignment in scalar context is the number of elements on its right.

Scalar context: one per call, and pos()

In scalar context — my $n = ..., a while condition, an if — the match finds the next occurrence, returns true, and stores where it ended in pos($log). That is what makes while ($log =~ /.../g) walk the string one request at a time. When a match fails, pos is reset to undef, so the next search starts from the beginning.

pos belongs to the variable, not to the value. A copy has a position of its own — which is how this page's first draft printed undef for every position: it passed $log to a helper, and the helper's copy had never been searched.

The if that starts in the wrong place

The first if finds POST and leaves pos at 19. The second if asks about /home — which is in the string, at the start — and fails, because it searched from 19. That failure resets pos, so the same question asked a third time succeeds. Two identical if statements, two different answers.

List context reads pos too: with pos left at 9 by the last if, $log =~ /GET \S+/g skips the first GET /home.

A match in an argument list

Function arguments are a list, so f($s =~ /re/) calls the match in list context. A successful match with no groups passes one argument (1); a failed one passes none — not 0, not ''. A helper with a one-argument signature dies on it. With /g it passes one argument per match.

While without /g

Without /g there is no position to move, so while ($log =~ /GET/) matches the first GET forever. The program stops it with a guard.

What to do instead

my @all  = $s =~ /re/g;               # every match
my $n    = () = $s =~ /re/g;          # how many
while ($s =~ /re/g) { ... pos($s) }   # walk, with positions
if ($s =~ /re/) { ... }               # a yes/no question: no /g
f(scalar($s =~ /re/));                # a yes/no passed to a function

If you are coming from another language

  • Python. The three uses are three functions — re.findall for all matches, re.finditer to walk, re.search for yes or no — and none keeps a hidden position between calls.
  • Java. Matcher.find() is scalar-context /g: the position lives in the Matcher object, so reusing one matcher for two questions has this page's if problem.

See also