CL Newsletter, May 2021 - Pattern Matching

If you’ve recently visited the updated Computation Layer documentation, you may have noticed a new section: pattern matching. That’s right. The highly sought after tools for parsing student expressions are finally here! We’ve made a short series of videos to help you learn more about pattern matching.

Get started with Scientific Patterns

This month, we’ll take some key points from those videos and present them as tips to help you get started.

A few notes before we jump in:

  1. You’ll probably use pattern matching very rarely or not at all. Don’t feel obligated to use it. Be more cautious of overuse than underuse.
  2. Pattern matching is more advanced than most of the topics we cover in our monthly newsletters. If you feel like you’re not yet ready to take that leap, don’t worry! We’ll save this newsletter in our archives for you to dig into later.

We don’t want anyone to leave empty handed, so if pattern matching isn’t something that interests you right now, Take a look at this interaction.

An animated gif of the interaction at

What do you like? What don’t you like? What would you change? How could you adapt it to work for something you teach? Let us know!

5 Tips for Making The Most Out of Patterns

  1. Avoid writing “patterns” over and over (watch video)

Start all of your pattern matching by creating a variable for the word “patterns.” (We like to use “p”.) That way all of this . . . :

Term1 = patterns.product(patterns.anyOf(patterns.fraction(patterns.integer,patterns.integer),patterns.number),patterns.exponent(patterns.literal(“x”),patterns.integer)) Term2 = patterns.product(paatterns.anyOf(patterns.fraction(patterns.integer,patterns.integer),patterns.number),patterns.literal(“x”)) Term3 = patterns.anyOf(patterns.fraction(patterns.integer,patterns.integer),p.number)

. . . can end up looking like this:

Term1 = p.product(p.anyOf(p.fraction(p.integer,p.integer),p.number),p.exponent(p.literal(“x”),p.integer)) Term2 = p.product(p.anyOf(p.fraction(p.integer,p.integer),p.number),p.literal(“x”)) Term3 = p.anyOf(p.fraction(p.integer,p.integer),p.number)

  1. Match, then parse (watch video)

Instead of trying to determine whether a string matches a pattern and taking out the part you want every time, determine that the pattern matches the string once, then parse out the pieces you need separately.

# Check to see that the string matches a sum or difference in the right form Match = p.anyOf(p.sum(p.product(Fraction,X),Number),p.difference(p.product(Fraction,X),Number)) # Parsing Slope and Intercept ParseSum = p.sum(p.product(p.expression,X),p.expression).parse(rhs) ParseDiff = p.difference(p.product(p.expression,X),p.expression).parse(rhs)

3. Write sub-patterns for commonly used parts (watch video)

Is there a piece of the pattern that you use over and over again? Turn it into a variable! You can place that variable into your pattern instead.

# General Use Patterns Number = p.anyOf(p.number,p.fraction(p.number,p.number),p.negative(p.fraction(p.number,p.number)),p.mixedNumber) Y = p.literal(“Y”) X = p.literal(“X”)

4. Parse sub-strings to save time (watch video)

Instead of writing a complicated specific pattern, pull out latex strings of part of a more general pattern, then match and parse each portion of the entire string separately as it becomes more specific.


5. Use sparingly and with caution (watch video)

There are so many great and reliable methods already in place to check for correctness. Pattern matching is a great tool for extracting parts of an input to build more comprehensive feedback, but it is brittle and can break if not done correctly. Instead, use error messages and warnings to gently nudge students toward the right form. Let each thing do what they do best.