0

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à 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.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.