Pattern Matching in Ruby
Overview
Ruby is a powerful programming language that supports pattern matching. Pattern matching is a powerful tool commonly found in functional programming languages. It is a mechanism for checking a value against a pattern. It allows developers to write clean and concise code. In this article, we will explore the different concepts related to pattern matching in Ruby, including its syntax, object, array, find, hash, alternative, variable patterns, and more.
What is Pattern Matching?
Pattern matching means looking for a specific pattern within a big chunk of data. It attempts to identify whether a specific set of items is present in the provided data. This technique only checks for an exact match, which means the pattern must be identical to the one you're looking for. It allows developers to write more clean and readable code. With the help of pattern matching, developers can match values against a pattern and extract specific data from the matched values.
Ruby Pattern Matching Syntax
The basic format for a pattern match is a case statement. It is not much different from the normal case statement that we are already familiar with. The only difference is that instead of when, here we use in. Let's look at the difference between both:-
Example (Normal Case Statement)
Example (Pattern Matching Case statement)
There are several types of patterns available in Ruby, including object or value patterns, array patterns, find patterns, hash patterns, alternative patterns, pattern or variable patterns, and variable binding patterns. Let's take a look at each of these patterns in detail.
Object or Value Pattern Match Syntax
Object or value pattern match syntax is used to match a value against a specific object or value. Any object can be used in the pattern match. It is matched using the === operator to compare the two objects. For example:-
In this example, the case statement matches against the val objects. Since the val object is an instance of the String class, the first condition is true and the corresponding block of code gets executed.
Let us look at another example where we use both values and objects:-
In this example, the case statement is matching against the num object. The first condition checks if the value of num equals 0. The second condition checks if the value of num lies between 1 to 5. The third condition checks if the num is an instance of the String class. Finally, if none of the above conditions get matched, then the else branch gets executed.
Array Pattern Match Syntax
Array pattern match syntax is used to match an array against a pattern. For example:
The first condition checks if the array is exactly [1, 2, 3] or not. This results in false. The second condition checks if the array is of size 3 and the first and third elements are 1 and 3 respectively. The variable x is just a placeholder showing that there will be one value at the position but that can be any value. So this condition gets satisfied and the corresponding code gets executed. Also, multiple such placeholders can be present in array pattern matching in Ruby.
The splat (\*) symbol is used to match against multiple entries. Let's look at an example:
In this example, the condition checks if the first and last element of the array is 1 and 10 respectively. There can be multiple elements in between these two.
Find Pattern Match Syntax
The find pattern match is used to match a part of an array. It works by placing a * on either side of the part you want to match. You can even use the variable pattern to give each * a variable name to reference later. Let’s look at an example:
The pre array consists of [1, 2, 1], and the post consists of [1]. The middle [2, 3] between pre and post gets matched here.
Hash Pattern Match Syntax
Hash pattern matching is a lot similar to the way array pattern matching works. The key difference is that matching is only possible for symbol keys and not string keys. We can match against the actual values of a hash. Let's look at this example:
It's important to note that when pattern matching against a hash, Ruby only checks if the keys in the pattern are present in the hash being matched, not if there are any extra keys in the hash that aren't in the pattern. This means that it's possible for a hash with additional keys to match a pattern as long as all the keys in the pattern are present with matching values. For example:
In this example, the first condition gets evaluated as true, so the control never reaches the second condition.
Alternative Pattern Match Syntax
An alternative pattern match is used to match a value against multiple patterns. For example:
In this example, we use the case statement to match the value against multiple integer values. The alternative pattern 5 | 10 checks if the value equals either 5 or 10. The corresponding message gets printed as the condition evaluates to true.
A pattern or Variable Pattern Match
A pattern or variable pattern match syntax is used to match any value and assign it to a variable. For example:
In the example above, we use the case statement to match the value of 10 against any pattern and assign it to the variable x. If the value matches, the corresponding message will be printed.
Variable Binding in Pattern Matching
Variable binding in pattern matching is used to assign a variable to a matched value. For example:
In this example, we use the case statement to match an array against the pattern [a, b, c]. Here, a is assigned the value 2, b gets assigned 3 and c gets assigned 6. Then we check if the product of a and b equals c. Since the condition is true, the corresponding message gets printed.
Variable Pinning in Pattern Matching
The ^ symbol before a variable name indicates variable pinning, which means that the value of the variable should remain constant and should not be reassigned during pattern matching in Ruby. For example:
In this example, the first condition evaluates to false as all the elements if the array is not equal. The second condition is true and hence the corresponding block gets printed.
Matching Non-Primitive Objects Using Deconstruct and Deconstruct_keys
Objects that are not primitive data types cannot be matched directly using pattern matching in Ruby. However, these objects can be matched using deconstruct or deconstruct_keys. For example:
In this example, an instance of Point is being created. Then it is first matched against an array. The Point#deconstruct gets called and is matched against the provided array. Then we match the object with the x: a, y: b pattern which calls the deconstruct_keys method.
Use Cases of Pattern Matching in Ruby
Pattern matching in Ruby can be used in various scenarios to make code more concise and expressive. Some use cases are:
- Destructuring values: Pattern matching in Ruby can be used to extract values from arrays, hashes, and objects in a concise and readable way. This can be useful when working with APIs that return complex data structures, or when parsing data from external sources.
- Case statements: Pattern matching in Ruby can be used to simplify case statements by providing a more expressive way to match patterns. This can make code more readable and maintainable.
- Guard clauses: Pattern matching in Ruby can be combined with guard clauses to add conditions to case statements. This can make code more concise and expressive.
- Error handling: Pattern matching in Ruby can be used in error handling to match exceptions against specific patterns and provide more informative error messages.
FAQs
Q: What is the difference between pattern matching and regular expressions?
A: Pattern matching in Ruby is a feature that allows us to match values against patterns and extract their values. Regular expressions, on the other hand, are a separate language that is used to match patterns in text.
Q: Is pattern matching available in earlier versions of Ruby?
A: Pattern matching was introduced in Ruby 2.7. Earlier versions of Ruby do not support pattern matching.
Q: Can pattern matching in Ruby be used with custom classes?
A: Yes, pattern matching in Ruby can be used with custom classes by defining a deconstruct or deconstruct_keys method that returns an array or hash with the object's attributes.
Conclusion
- Pattern matching in Ruby is a powerful feature.
- It allows us to match values against patterns and extract their values in a concise and readable way.
- Pattern matching in Ruby can be used in various scenarios, including:
- Destructuring values
- Simplifying case statements
- Adding guard clauses
- Error handling
- By understanding the syntax and use cases of pattern matching in Ruby, we can write more expressive and maintainable code.