Pattern Matching trong Ruby cho phép giải cấu trúc dữ liệu một cách ngắn gọn, giúp dễ dàng gán giá trị cho các biến với cú pháp rõ ràng. Pattern matching là một tính năng đã được giới thiệu trong Ruby 2.7. Từ Ruby 3.0 trở đi, nó không còn là một tính năng thử nghiệm nữa và chúng ta có thể bắt đầu sử dụng nó một cách chính thức.
Warning Ruby 2.7: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
Pattern Matching là gì?
Pattern matching là một tính năng cho phép bạn so sánh và hiểu cấu trúc dữ liệu. Điều này được thực hiện bằng cách kiểm tra cách thông tin được tổ chức và gán các phần tương ứng cho các biến cục bộ để sử dụng sau này.
Pattern Matching được hỗ trợ thông qua cú pháp case / in. Quan trọng là không nhầm lẫn với case / when và không kết hợp chúng. Nếu không có sự phù hợp với bất kỳ biểu thức nào và không có định nghĩa else
, thì một ngoại lệ NoMatchingPatternError
sẽ được raise
case <expression>
in <pattern1>
# ...
in <pattern2>
# ...
else
# ...
end
Pattern có thể là:
- Value: Any Ruby object (compared with the === operator, as in ‘when’).
- Array: Array pattern:
[<subpattern>, <subpattern>, <subpattern>, <subpattern>, ...]
. - Find: Search pattern:
[*variable, <subpattern>, <subpattern>, <subpattern>, <subpattern>, ..., *variable]
. - Hash: Hash pattern:
{key: <subpattern>, key: <subpattern>, ...}
. - Alternative: Pattern combination with
|
(vertical bar). - Variable capture:
<pattern> => variable
orvariable
.
Định nghĩa Pattern Matching
# Define a method that uses pattern matching with case/in
def process_data(data)
case data
in { type: "number", value: Integer => num }
puts "Received a number: #{num}"
in { type: "string", value: String => str }
puts "Received a string: #{str}"
in { type: "array", value: Array => arr }
puts "Received an array: #{arr}"
in { type: "hash", value: Hash => hash }
puts "Received a hash: #{hash}"
else
puts "Received something else."
end
end
# Test the method with different data structures
process_data({ type: "number", value: 42 }) # Output: Received a number: 42
process_data({ type: "string", value: "Hello, Ruby!" }) # Output: Received a string: Hello, Ruby!
process_data({ type: "array", value: [1, 2, 3] }) # Output: Received an array: [1, 2, 3]
process_data({ type: "hash", value: { key: "value" } }) # Output: Received a hash: {:key=>"value"}
process_data({ type: "unknown", value: "unknown data" }) # Output: Received something else.
Deconstruct và Deconstruct_keys
Đây là hai phương thức đặc biệt trong pattern matching: deconstruct
, được gọi khi đánh giá trên một Array, và deconstruct_keys
được gọi khi đánh giá trên một Hash. Hãy xem một ví dụ:
class Coordinates
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
def deconstruct
[@x, @y]
end
def deconstruct_key
{x: @x, y: @y}
end
end
Khi một thể hiện của lớp Coordinate được đánh giá trên một Array
c = Coordinates.new(32,50)
case c
in [a,b]
p a #=> 32
p b #=> 50
end
Khi một thể hiện của lớp Coordinate được đánh giá trên một Hash
case c
in {x:, y:}
p x #=> 32
p y #=> 50
end
Nếu class không có 2 methods trên thì sẽ báo lỗi
#<Coordinate:0x000000010d27fb10 @x=32, @y=50>: #<Coordinate:0x000000010d27fb10 @x=32, @y=50> does not respond to #deconstruct (NoMatchingPatternError)
Tham khảo thêm tại: https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html