So sánh giữa nil? và == nil
Bài đăng này đã không được cập nhật trong 6 năm
Có điểm gì khác biệt giữa nil? và == nil
Sẽ không có gì khác biệt khi mà bạn nhìn vào kết quả trả về. Và tôi thích dùng nil? vì nó dễ đọc hơn. Nhưng đó chỉ là vấn đề về cảm nhận của mỗi người
Tuy nhiên có một sự khác biệt nhỏ trong cách tính kết quả này.
1. nil?
nil? là một phương thức được định nghĩa trên Object và NilClass.
# NilClass
rb_true(VALUE obj)
{
return Qtrue;
}
# Object
static VALUE
rb_false(VALUE obj)
{
return Qfalse;
}
Kiểm tra nil?
chỉ là việc gọi một phương thức đơn giản. Đối tượng của bạn kế thừa từ class Object đã được cài đặt phương thức nil? và luôn trả về false
Object.new.nil? # return false
Lưu ý: Trong lớp cha của lớp Object là lớp BasicObject, phương thức nil? không được cài đặt
BasicObject.new.nil? # undefined method `nil?' for #<BasicObject:0x000000065e7940>
2. == nil
a == b
chỉ là cú pháp để gửi thông điệp == từ phải sang trái cùng với một đối số duy nhất. Điều đó có nghĩa là a.==(b)
đối với trường hợp tổng quát. Còn trong trường hợp cụ thể của chúng ta a.==(nil)
== là một phương thức được định nghĩa trên BasicObject:
rb_obj_equal(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2) return Qtrue;
return Qfalse;
}
Từ tài liệu
Ở mức đối tượng, == trả về true chỉ khi giá trị so sánh và giá trị được so sánh có cùng một đối tượng
Vì vậy kết quả trả về true nếu hai biến đang trỏ đến cùng một đối tượng hoặc nếu lớp nhận đã ghi đè phương thức == theo cách khác. Và các lớp con được dùng để ghi đè == để thực hiện hành vi cụ thể của lớp. Điều đó có nghĩa là hiệu suất phụ thuộc vào việc thực hiện ==. Không giống như nil? không được ghi đè bằng các lớp con.
Hơi khó hiểu nhỉ. Để tôi giải thích điều này
a = :framgia
b = :framgia
a == b # return true
a.object_id # return 9107228
b.object_id # return 9107228
c = "framgia"
d = "framgia"
c == d # return true
c.object_id # return 53695100
d.object_id # return 38048440
Cả 2 phép so sánh đều trả về true. Nhưng a, b cùng thuộc 1 đối tượng vì có cùng object_id. Còn c, d không thuộc cùng một đối tượng vì ko cùng object_id, tức là c == d đã ghi đè phương thức ==
3. Giải pháp khác
Trong ruby, để kiểm tra mọi thứ người ta hay đem ra so sánh với false
hoặc nil
.Nhưng bạn cũng có thể bỏ qua việc so sánh này bằng cách kiểm tra chính đối tượng mà bạn đang dùng
if some_object
puts "some_object is neither nil nor false" # Một vài đối tượng không phải nil cũng chẳng phải false
end
4. Hiệu năng
Tôi mong ước nil? có thể nhanh được như == nil. Bởi vì hiệu suất việc ghi đè == phụ thuộc vào receiver. Đây là một benchmark đơn giản để kiểm tra các giả định của tôi. Như thường lệ tôi sử dụng gem benchmark/ips của Evan Phoenix.
require 'benchmark/ips'
class ExpensiveEquals
def ==(other)
1000.times {}
super(other)
end
end
string = 'something'
number = 123
expensive = ExpensiveEquals.new
notnil = Object.new
isnil = nil
Benchmark.ips do |x|
x.report('isnil.nil?') do
if isnil.nil?
end
end
x.report('notnil.nil?') do
if notnil.nil?
end
end
x.report('string.nil?') do
if string.nil?
end
end
x.report('number.nil?') do
if number.nil?
end
end
x.report('expensive.nil?') do
if expensive.nil?
end
end
x.report('isnil == nil') do
if isnil == nil
end
end
x.report('notnil == nil') do
if notnil == nil
end
end
x.report('string == nil') do
if string == nil
end
end
x.report('number == nil') do
if number == nil
end
end
x.report('expensive == nil') do
if expensive == nil
end
end
x.report('nil == isnil') do
if nil == isnil
end
end
x.report('nil == notnil') do
if nil == notnil
end
end
x.report('nil == string') do
if nil == string
end
end
x.report('nil == number') do
if nil == number
end
end
x.report('nil == expensive') do
if nil == expensive
end
end
x.report('isnil') do
if isnil
end
end
x.report('notnil') do
if notnil
end
end
x.report('string') do
if string
end
end
x.report('number') do
if number
end
end
x.report('expensive') do
if expensive
end
end
x.compare!
end
Kết quả trả về:
notnil: 11840655.4 i/s
expensive: 11801608.9 i/s - 1.00x slower
number: 11679119.8 i/s - 1.01x slower
string: 11598016.4 i/s - 1.02x slower
isnil: 11529034.6 i/s - 1.03x slower
notnil == nil: 10397262.7 i/s - 1.14x slower
nil == expensive: 10319767.0 i/s - 1.15x slower
nil == string: 10188393.9 i/s - 1.16x slower
nil == number: 10167930.4 i/s - 1.16x slower
isnil == nil: 10120560.0 i/s - 1.17x slower
nil == notnil: 10069779.1 i/s - 1.18x slower
nil == isnil: 10055165.2 i/s - 1.18x slower
expensive.nil?: 9970905.5 i/s - 1.19x slower
string.nil?: 9967045.3 i/s - 1.19x slower
number.nil?: 9893974.5 i/s - 1.20x slower
notnil.nil?: 9581974.5 i/s - 1.24x slower
isnil.nil?: 8390963.6 i/s - 1.41x slower
string == nil: 6915330.1 i/s - 1.71x slower
number == nil: 6803969.3 i/s - 1.74x slower
expensive == nil: 25382.6 i/s - 466.49x slower
chú thích: i/s là iterations/second
Theo như kết quả thì phép toán chạy chậm nhất expensive == nil
chậm gấp 466.49 lần phép toán chạy nhanh nhất notnil
(notnil
được lấy ra làm đơn vị cơ bản để so sánh slower với các phép toán còn lại)
Tôi không mong muốn nil? lại chạy chậm đến vậy. Qua kết qua thu được thì việc kiểm tra nil ==
sẽ cho hiệu năng cao là bạn kiểm tra == nil
. Và trong các phép kiểm tra thì kiểm tra chính đối tượng như được nêu ra ở phần 3. giải pháp khác đã đạt được hiệu năng cao nhất
5. Kết luận
Bài viết này mình dịch từ nguồn: http://pascalbetz.github.io/ruby/2016/05/17/nilvsequals/
Mong nhận được sự đóng góp ý kiến của các bạn
All rights reserved