Fun with Ruby Pattern Matching
Pattern matching emerged in experimental status in Ruby 2.7, I got my first chance to properly play with an implementation that uses it.
I love playing Advent of Code: every December a code puzzle every day of advent. This year's Day 12 has you processing a series of instructions each of which have an integer value attached.
There are seven commands, N
, E
, S
, W
, L
, R
anf F
. Here's what they look like (from the published example):
F10
N3
F7
R90
F11
I'll let you have a little read of the puzzle text if you want the detail but in essence, the compass cardinal points move you in that direction. L
and R
rotate you a number of degrees (always 90, 180 or 270) and F
moves you forward in the direction you are currently facing.
At this point, I've parsed the instructions out into an array of arrays, like this:
[
["F", 10],
["N", 3],
["F", 7],
["R", 90],
["F", 11]
]
There's lots of ways I can skip through matching commands but pattern matching makes this very readable:
steps.each do |step|
case step
in 'N', val
# val equals step[1] here
in 'S', val
# val equals step[1] here
end
end
I can both do something conditionally and bind part of the array to a variable. Very nice.
Now, in part 2 of the puzzle (which you can't see unless you solve part 1) I needed to do much the same but I also needed to treat two of the commands the same and change behaviour according to the value of val
. I've seen some mention of using alternative patterns in matching but wasn't quite sure how this worked. For my case, it was perfect, here's some code:
steps.each do |step|
case step
in 'N', val
# as before
in 'S', val
# as before
in 'L', 90
# match only ["L", 90]
in 'L' | 'R', 180
# match either ["L", 180] OR ["R", 180]
in 'L', 270
# match only ["L", 270]
in 'R', 90
# match only ["R", 90]
in 'R', 270
# match only ["R", 270]
# ...
end
end
Now that's pretty awesomely powerful, especially line 9 and saves the world from a mass of nested if
s.
Member discussion