12/08/2018, 14:23

Phân loại so sánh bằng và cách nối chuỗi trong Ruby

Phân loại so sánh bằng trong ruby Như chúng ta đã biết so sánh bằng có các loại là : == , ===, eql?, equal? Giống nhau : Điểm giống nhau cơ bản giữa chúng là chúng đều dùng để so sánh và trả về giá trị là true hoặc false. Khác nhau : == ( generic equality ): So sánh có cùng giá trị hay ...

  1. Phân loại so sánh bằng trong ruby Như chúng ta đã biết so sánh bằng có các loại là : == , ===, eql?, equal?

Giống nhau : Điểm giống nhau cơ bản giữa chúng là chúng đều dùng để so sánh và trả về giá trị là true hoặc false.

Khác nhau :

  • == ( generic equality ): So sánh có cùng giá trị hay không. Đây là cách so sánh phổ biến và cơ bản nhất trong hầu hết các ngôn ngữ lập trình.
  • === ( Case equality ): Như tên gọi của nó, === là so sánh dạng case. Các điều kiện của case sẽ đc implement với mỗi class tương ứng. Ví dụ với:
    • Range
    • Regex
    • Proc (in Ruby 1.9)

Ví dụ:

case some_object
when /a regex/
# The regex matches
when 2..4
# some_object is in the range 2..4
when  lambda {|x| some_crazy_custom_predicate }
# the lambda returned true
end
(1..5) === 3           # => true
(1..5) === 6           # => false
Integer === 42          # => true
Integer === 'fourtytwo' # => false
/ell/ === 'Hello'     # => true
/ell/ === 'Foobar'    # => false
"a" === "b" # false # different values, different objects
"a" === "a" # true # same values

Như vậy === cũng đơn thuần là so sánh giá trị chứ không phải là so sánh object có điều nó dùng case để so sánh và === còn được gọi là Case equality.

"test" == "test"  #=> true
"test" === "test" #=> true
#
#sự khác nhau giữa == và === là ở đây
String === "test"   #=> true
String == "test"    #=> false
  • Eql? — Hash equality: Method này sẽ trả về true nếu 2 tham số có cùng hash key. Nó sử dụng Hash để kiểm tra các member có giống nhau hay không. Với Object thì Eql? giống như ==(chỉ so sánh value) và hầu hết các subclasses cũng như vậy.

Tuy nhiên với kiểu Numeric thì có ngoại lệ, nó chỉ kiểm tra 2 tham số có cùng giá trị và cùng Class hay không.

Ví dụ:

1 == 1.0     #=> true   |  vì == chỉ so sánh giá trị
1.eql? 1.0   #=> false  |  vì chúng khác Class
1.class      #=> Fixnum
1.0.class    #=> Float
  • Equal? — So sánh giá trị và object_id của object.

object_id là method trả về định danh của đối tượng. Nếu hai đối tượng có chung một object_id thì chúng giống nhau tức là đều trỏ đến cùng một đối tượng trong vùng nhớ và ngược lại.

Equal? là cách hiệu quả để so sánh con trỏ, dùng để kiểm tra chính xác xem có cùng là 1 Object hay không. để hiểu rõ hơn ta xem ví dụ sau:

2.1.0 :001 > a = "viblo.asia"
2.1.0 :002 > b = "viblo.asia"
2.1.0 :003 > c = d = "viblo.asia"
2.1.0 :004 > a.equal?b    # false
2.1.0 :005 > c.equal?d    # true

Bây giờ ta cùng xem object_id của chúng

2.1.0 :005 > a.object_id  # 19088820
2.1.0 :007 > b.object_id  # 19008500
2.1.0 :008 > c.object_id  # 18854700
2.1.0 :009 > d.object_id  # 18854700

Ta thấy c và d có cùng object_id là 18854700 và kết quả trả về là false.

Như vậy Equal? sẽ so sánh cả object_id nên mặc dù value của a, b, c, d cùng là viblo.asia nhưng chúng có object_id khác nhau dẫn đến kết quả trả về là false.

2.SỰ KHÁC NHAU CỦA << & += TRONG VIỆC NỐI CHUỖI

  • Nối chuỗi với +=
2.1.0 :010 > first_name = "Viblo"
2.1.0 :011 > name = first_name
2.1.0 :012 > last_name = "Asia"
2.1.0 :013 > name += last_name
2.1.0 :014 > name # VibloAsia
2.1.0 :015 > first_name # Viblo
  • Nối chuỗi với <<
2.1.0 :010 > first_name = "Viblo"
2.1.0 :011 > name = first_name
2.1.0 :012 > last_name = "Asia"
2.1.0 :013 > name << last_name
2.1.0 :014 > name # VibloAsia
2.1.0 :015 > first_name # VibloAsia

kiểm tra object_id của name, first_name

2.1.0 :016 > name.object_id --> 15533700
2.1.0 :017 > first_name.object_id --> 15533700

Phép gán name = first_name lúc này name & first_name có cùng 1 object_id và khi thực hiện:

    • << sẽ xử lý trực tiếp trên object của nó, vì vậy khi name thay đổi thì first_name cũng thay đổi theo.
    • += sẽ tạo ra một object mới sau khi thực hiện cộng chuỗi xong mới gán lại giá trị cho object cũ, vì vậy khi thay đổi name thì first_name không bị ảnh hưởng.

Chúng ta cùng sử dụng benchmark để xem thời gian thực hiện của 2 cách này:

require 'benchmabash'
n = 10000
Benchmark.bm do |benchmark|
    benchmark.report("+=") do
        n.times do
         first_name = "Viblo"
         name = first_name
         last_name = "Asia"
         name += last_name
        end
    end
    benchmark.report("<<") do
        n.times do
         first_name = "Viblo"
         name = first_name
         last_name = "Asia"
         name << last_name
        end
    end
end

kết quả

   user     system      total        real
+=  0.010000   0.000000   0.010000 (  0.007283)
<<  0.000000   0.000000   0.000000 (  0.004042)

Qua đó mình thấy được tốc độ xử lý của << sẽ nhanh hơn +=. Đây cũng chính là lý do << được sử dụng phổ biến hơn

0