Toán tử === trong Ruby
Gần đây công việc của tôi hay dùng tới với module Enumerable của Ruby. Và tôi cố gắng cân bằng giữa việc học các API phổ biến nhất (mà có thể bạn đã biết) với các API ít phổ biến hơn nhưng rất hữu ích. Qua đó tôi cũng học được rất nhiều điều thú vị. Sau đây tôi sẽ chia sẻ một trong số những điều mà ...
Gần đây công việc của tôi hay dùng tới với module Enumerable của Ruby. Và tôi cố gắng cân bằng giữa việc học các API phổ biến nhất (mà có thể bạn đã biết) với các API ít phổ biến hơn nhưng rất hữu ích. Qua đó tôi cũng học được rất nhiều điều thú vị. Sau đây tôi sẽ chia sẻ một trong số những điều mà tôi cảm thấy hứng thú và đáng chú ý. Chúng ta hãy xét ví dụ sau về method grep của module Enumerable
# grep(pattern) → array # Returns an array of every element in enum # for which Pattern === element. (1..100).grep(38..44) #=> [38, 39, 40, 41, 42, 43, 44] names = %w( William Kate Adam Alexa James Natasha ) names.grep(/am/) # => %w(William Adam James)
Như ví dụ trên ta nhận thấy là method grep hoạt động với bất kỳ class nào có toán tử ===, điều này làm tôi tò mò là những class nào thực hiện nó và nó thực hiện bằng cách nào. Hãy cùng tìm hiểu.
Class/Module
mod === obj #→ true or false
=== trả về true khi obj là 1 thể hiện của mod hoặc hậu duệ của mod Nó về cở bản giống với method kind_of?:
obj.kind_of?(mod)
Ví dụ
"text".class.ancestors # => [String, Comparable, Object, Kernel, BasicObject] String === "text" # => true Object === "text" # => true Comparable === "text" # => true Numeric === "text" # => false
Regexp
rxp === str #→ true or false
Ở trường hợp của Regexp, === giống với:
rxp =~ str >= 0
Ví dụ
/^[a-z]*$/ === "HELLO" #=> false /^[A-Z]*$/ === "HELLO" #=> true
Range
rng === obj #→ true or false
trả về true nếu obj là phần tử thuộc rng, ngược lại trả về false Ví dụ
(Date.new(2017, 8, 21)..Date.new(2017, 8, 27)) === Date.new(2017, 8, 27) # => true (Date.new(2017, 8, 21)..Date.new(2017, 8, 27)) === Date.new(2017, 8, 29) # => false ("a".."z") === "a" # => true ("a".."z") === "abc" # => false
Proc
proc === obj # → result_of_proc
Gọi khối lệnh trong proc với params là obj Ví dụ
is_today = -> (date) { Date.current === date } is_today === Date.current # => true is_today === Date.tomorrow # => false is_today === Date.yesterday # => false
Object
Đối với hầu hết các object thì toán tử === giống với ==
Your class
Có một câu hỏi là nếu bây giờ chúng ta tự tạo ra class riêng và khai báo method === thì khi dùng grep sẽ như thế nào. Chúng ta xét ví dụ sau:
class State def initialize(expected_state) @expected_state = expected_state end def ===(obj) obj.state.to_s == @expected_state.to_s end end class Order < Struct.new(:id, :state, :customer_name) end p = Order.new(1, "placed", "Robert") v = Order.new(2, "verified", "Anne") s = Order.new(3, "shipped", "Kate") orders = [p,v,s] verified = State.new(:verified) placed = State.new(:placed) verified === p # => false orders.grep(verified) # => [#<struct Order id=2, state="verified", customer_name="Anne">] message = case v when verified "Your order has been verified and is awaiting shippment" when placed "Please wait for verification" else "---" end # => "Your order has been verified and is awaiting shippment"
Qua ví dụ trên ta thấy có thể dùng thể hiện của class mình tạo ra match với cả trường hợp dùng case...when và trường hợp Array#grep
Your object
Ruby cho phép bạn định nghĩa một phương thức Singleton chỉ ảnh hưởng đến hành vi của một đối tượng duy nhất, thậm chí không cần class. Ví dụ
VERIFIED = Object.new def VERIFIED.===(obj) obj.state.to_s == "verified" end class Order < Struct.new(:id, :state, :customer_name) end VERIFIED === Order.new(1, "placed", "Robert") # => false VERIFIED === Order.new(2, "verified", "Rita") # => true
Nhưng tốt hơn thì nên dùng Proc cho trường hợp này
VERIFIED = -> (obj){ obj.state.to_s == "verified" }
Reference
Nguồn: http://blog.arkency.com/the-equals-equals-equals-case-equality-operator-in-ruby/ Mong rằng bài viết này giúp bạn hiểu và thấy hứng thú hơn với Ruby. Thanks.