String and Symbol in Ruby
Bài đăng này đã không được cập nhật trong 6 năm
Ruby có Symbol cũng như String. Symbol
trong Ruby là một khái niệm khá thú vị và được sử dụng rất nhiều.
Lập trình viên chúng ta chắc hẳn đã qúa quen với symbol
hay string
khi sử dụng trong các task công việc. Nhưng có khi nào, ta tự hỏi symbol
và string
nó khác nhau như nào? Tại sao khi thì dùng symbol
, lúc lại sử dụng string
. Và dùng chúng trong trường hợp nào là tốt nhất?
Ở bài viết này chúng ta cùng đi tìm hiểu về sự khác nhau giữa symbol
và string
để sử dụng đúng hơn trong khi coding.
What is Symbol?
Symbol
là một khái niệm hơi khó hiểu với những người mới bắt đầu với Ruby on Rails hay thậm chí trong cả ngôn ngữ lập trình khác. Có người cho rằng nó chỉ là một biến, hoặc chỉ là một cái tên, nhưng symbol
lại không hề đơn giản như vậy.
Chúng ta có thể hiểu symbol
như là an object with a name
.
Difference between Ruby String and Symbol:
1. String is mutable, but Symbol isn’t.
symbol
giống với string
ở chỗ, symbol
cũng có một số method như là: length
、upcase
、downcase
... Tuy nhiên, chúng lại khác nhau ở chỗ string
có thể thay đổi được - tức là chúng ta có thể thay đổi nội dung của một chuỗi nếu muốn, nhưng với symbol
điều đó là không thể.
# just like string, you can use length method to count the letters.
>> :hello.length
=> 5
# or upcase method to return a symbol with all capital letters.
>> :hello.upcase
=> :HELLO
# let's say we have a "hello" string,
# we can use brackets with index number to get the letter.
>> "hello"[0]
=> "h"
>> "hello"[3]
=> "l"
# and so can symbol
>> :hello[0]
=> "h"
>> :hello[3]
=> "l"
# we can also use brackets with index number to change the letter
>> "hello"[0] = "k"
=> "k"
# but it doesn't work on symbol. because symbol doesn't have the []= method
>> :hello[0] = "k"
NoMethodError: undefined method `[]=' for :hello:Symbol'`
Vì vậy, bạn cũng có thể nghĩ rằng symbol
như một loại immutable string(chuỗi bất biến).
2. Symbol has better performance
Trong Ruby, khi bạn tạo một chuỗi mới, nó sẽ yêu cầu Ruby phân bổ bộ nhớ mới cho nó, như sau:
5.times do
puts "hello".object_id
end
19661060
19660980
19660920
19660860
19488720
=> 5
Phương thức object_id sẽ trả về số thứ tự duy nhất trong Ruby, nó sẽ thay đổi theo máy tính khác nhau hoặc phiên bản Ruby.
Trong thế giới Ruby, cùng một đối tượng sẽ có object id giống nhau, và các đối tượng có cùng một object id có nghĩa là chúng cùng một đối tượng.
Và bạn có thể thấy ví dụ trên, chuỗi "hello" có object id khác nhau và chiếm một số không gian bộ nhớ, có nghĩa là chúng là 5 đối tượng khác nhau trong Ruby.
Và, khi dùng symbol
, kết qủa ta nhận được lại như sau:
5.times do
puts :hello.object_id
end
1098268
1098268
1098268
1098268
1098268
=> 5
Kết quả cho thấy những đối tượng trên có cùng một object id, nghĩa là chúng đang cùng một đối tượng.
Khi sử dụng symbol
:hello lần đầu tiên, Ruby sẽ cấp phát bộ nhớ và tạo ra symbol này cho bạn, khi bạn cố gắng truy cập vào đó một lần nữa, Ruby sẽ lấy lại nó từ bộ nhớ thay vì tạo ra mới, vì vậy symbol
sẽ ít sử dụng bộ nhớ hơn. Và điều này giảm được việc lãng phí bộ nhớ.
Mặc dù symbol
tiết kiệm bộ nhớ, nhưng những phiên bản Ruby <= 2.2, bộ nhớ không thể được tái sử dụng tự động, bạn có thể phải khởi động lại ứng dụng để giải phóng bộ nhớ đó, do đó có thể gây rò rỉ bộ nhớ nếu bạn tạo nhiều symbol
. Từ ruby 2.2 trở đi, cơ chế Symbol GC (Garbage Collection)
đã được giới thiệu, những ký hiệu đã được tạo ra bởi to_sym hoặc các phương pháp thực hiện có thể được tái sử dụng giống như các đối tượng khác.
- Ngoài ra, ở ví dụ trên, nếu ta sử dụng
freeze
chostring
, chúng ta cũng sẽ nhận được một object id duy nhất.
3. Comparsion of symbols is faster than strings
Ta có ví dụ như sau:
require 'benchmark'
loop_times = 100000000
str = Benchmark.measure do
loop_times.times do
"hello" == "hello"
end
end.total
sym = Benchmark.measure do
loop_times.times do
:hello == :hello
end
end.total
puts "Benchmark"
puts "String: #{str}"
puts "Symbol: #{sym}"
# => Benchmark
# => String: 29.450000000000003
# => Symbol: 7.889999999999999
Như ta thấy, so sánh giữa các symbol
nhanh hơn nhiều so với chuỗi, đó là bởi vì các symbol
chỉ so sánh nếu chúng là cùng một đối tượng (có id đối tượng giống nhau).
String and Symbol are convertable
Class String
và Symbol
có support một số method để covert lẫn nhau
# to_sym method can convert string to symbol
>> "name".to_sym
=> :name
# or intern method, it's identical with to_sym method
>> "name".intern
=> :name
# you can also use literal notation %s
>> %s(name)
=> :name
# to_s can convert symbol to string
>> :name.to_s
=> "name"
# id2name method do the same thing with to_s
>> :name.id2name
=> "name"
When should use symbol?
1. Symbol as the key of Hash
profile = { name: "eddie", age: 18 }
=> {:name=>"eddie", :age=>18}
Bởi vì không symbol
là chuỗi bất biến, không thay đổi và việc tra cứu, cũng như so sánh hiệu suất vốn nhanh hơn so với string
nên nó rất thích hợp để làm keys
của Hash
.
2. String has more powerful and useful methods than Symbol
Mặc dù bạn có thể convert symbol
sang string
, nhưng sau khi tất cả các symbol không phải là string nên nó sẽ không có các phương pháp nhiều như string. Do đó, nếu bạn muốn sử dụng những phương pháp hữu ích của lớp String, thì nên chọn String.
3. Use String or Symbol as parameters?
Ví dụ:
class Cat
attr_accessor :name
end
kitty = Cat.new
kitty.name = "Nancy"
puts kitty.name # => Nancy
Ở ví dụ trên, ta có thể thay thế attr_accessor :name
bằng attr_accessor "name"
đều được.
Một số method thì dùng symbol
như parameters
, một số khác lại sử dụng string
. Và đôi khi, có những method đều có thể sử dụng cả hai.
Thanks for your reading!
All rights reserved