Lazy Patterns
The default notation is ECMAScript. The notation can be adjusted to match other standards, so the first question when reading an unfamiliar pattern is which notation's rules apply.
Why Regular Expressions Feel Write-Only
Many learners meet regular expressions and conclude that they are a kind of code that only its author can ever read. A single line can hold a dense cluster of symbols, and once it is written, it seems to resist any later review. The source for this topic names this belief directly: regular expressions can easily become a write-only language. The useful question is not whether that danger exists, but what habits keep a pattern readable and what the notation itself gives you to work with.
Before reading further, pause and ask yourself: what would make a pattern easy for a future reader to understand, and what would make it impossible? Keep your answer in mind. The rest of this article tests that intuition against what the source gives.
The Default Notation and Its Adjustment
A regular expression notation is a set of rules about which symbols mean what. Every regular expression is written in some notation, and the rules of that notation determine how you read it.
The default regular expression notation described in this topic is that of ECMAScript. Unless you choose otherwise, the rules you are reading are the ECMAScript rules.
The notation is not fixed to that one default. The source states that it can be adjusted to match various standards. The same general idea of a pattern can therefore be expressed under different notational conventions, and you select the convention that your situation requires. What changes between conventions is the set of rules for reading the symbols, not the underlying idea of describing text by pattern.
The default notation is ECMAScript. The notation can be adjusted to match other standards, so the first question when reading an unfamiliar pattern is which notation's rules apply.
Why Dense Patterns Resist Reading
The belief that regular expressions are unreadable is partly true, because a regular expression packs a lot of meaning into very few characters. A reader has to recognize each symbol, understand how symbols combine, and keep track of which part of the pattern each character belongs to. When a pattern grows without discipline, these combinations become hard to follow, even for the person who wrote them.
The practical correction is restraint. Do not write the most compact pattern that happens to work. Write the pattern that a later reader can verify at a glance. The source gives this as a tip for using regular expressions effectively, and it applies to every pattern you write, whether short or long.
Common mistake Choosing the shortest pattern because it works
Why it failsA pattern that works but cannot be checked at a glance will be hard for a later reader to verify, and that defeats the purpose of writing it down.
Do this insteadWrite the pattern that a later reader can verify at a glance, even if it is a little longer.
Common mistake Assuming the notation is fixed
Why it failsThe notation can be adjusted to match various standards, so the same symbols may not carry the meaning you expect.
Do this insteadIdentify which notation is in use before reading or writing a pattern.
Reusing a Subpattern with \i
The notation includes \i, which the source describes as allowing you to express a subpattern in terms of a previous subpattern. Instead of spelling out the same subpattern again, you refer back to an earlier one. The result reads as a composition of named ideas rather than one long sequence of symbols.
The Question Mark That Makes a Pattern Lazy
The source gives one specific modifier: the question mark ?. Placing ? in a pattern makes that pattern lazy. A lazy pattern matches as little text as it can while still satisfying the pattern, as opposed to matching as much as possible. Because the modifier is a single symbol, the change it makes is easy to overlook when you read a pattern.
What do you think happens?
If a pattern would otherwise match a long stretch of text, would the lazy version match a longer or a shorter stretch?
Reveal answer
A shorter stretch
A lazy pattern matches as little text as it can while still satisfying the pattern, as opposed to matching as much as possible.
To see the difference concretely, consider a generated illustration. Suppose the input text is the string <b>one</b> and <b>two</b>, and the pattern is written as <b>.*</b> with the star matching as much as possible, compared with <b>.*?</b> where the question mark makes the pattern lazy. The diagram below shows where each version ends its match on the same input. This is an original illustration of the lazy-versus-greedy idea the source describes, not an example taken from the source.
In the greedy version, one match covers everything from the first opening tag through the last closing tag. In the lazy version, the match stops at the first place the pattern can be satisfied, so the same input yields two separate matches. The question mark did not change the characters being searched for; it changed how much text each match is allowed to take.
Iterating Over a Stream with Regular Expressions
Regular expressions are not only used to test a single string. The source explains that you can use regex iterators to iterate over a stream while looking for a pattern. The pattern is applied repeatedly as you move through a stream of text, and each match the iterator finds is a point where your program can act.
Tracing an iterator over a short stream
A generated stream of text reads: cat, dog, cat. The pattern is cat. Trace how an iterator moves through the stream.
- 1Start at the beginning
The iterator begins at the start of the stream and searches for the pattern cat.
- 2First match
The first cat is found. Your program can act on this match.
- 3Resume after the match
The search continues from where the first match ended, not from the start of the stream again.
- 4Second match
The search finds the second cat later in the stream. Again, the program can act on it.
- 5Exhaust the stream
No further matches remain, so the iteration ends.
The iterator yields two matches, one for each occurrence of cat, and then stops. This trace is an original illustration of the process the source describes; the source names the iterator and its purpose but does not spell out every step.
Practicing the Lazy Habit
Using the generated input <b>one</b> and <b>two</b>, state where the greedy pattern <b>.*</b> ends its match and where the lazy pattern <b>.*?</b> ends its first match. Then explain in one sentence why the lazy version produces more than one match when the iterator runs over the whole input.
Hints
- Recall that a lazy pattern matches as little text as it can while still satisfying the pattern.
- Think about where the first closing tag appears in the input.
- Remember that the iterator resumes searching after each match ends.
Mistakes When Reading Lazy Patterns
Common mistake Overlooking the question mark
Why it failsThe ? modifier is a single symbol, so it is easy to miss, yet it changes how much text the pattern matches.
Do this insteadCheck every quantifier for a trailing ? before deciding what a pattern will match.
Common mistake Assuming a lazy pattern searches for different text
Why it failsThe ? changes how much text is matched, not which characters the pattern looks for.
Do this insteadKeep the matched characters in mind separately from the length of the match.
Common mistake Treating an iterator as a single test
Why it failsAn iterator applies the pattern repeatedly across a stream, so one pattern can produce several matches.
Do this insteadExpect multiple match points and decide how your program should act on each one.
Key Takeaways
- The default regular expression notation is ECMAScript, and the notation can be adjusted to match various standards.
- Regular expressions can become a write-only language, so restraint is the practical correction: write the pattern a later reader can verify at a glance.
- The ? modifier makes a pattern lazy, so it matches as little text as it can while still satisfying the pattern, as opposed to matching as much as possible.
- A regex iterator moves through a stream of text, applying the pattern repeatedly and giving your program a point to act on at each match.
- The \i notation lets one subpattern be expressed in terms of a previous subpattern, so a pattern can read as a composition of named parts.