Sự khác nhau giữa Symbols và Strings
Bài đăng này đã không được cập nhật trong 6 năm
Có rất nhiều người thắc mắc sự khác nhau giữa Strings
và Symbol
. Vậy, dưới đây chúng ta hãy cùng nhau tìm hiểu về điều này.
Strings
được sử dụng để làm việc với data
Symbol
thường được dùng để định danh.
Đó là điểm khác biệt chính của Strings và Symbol. Symbol không chỉ là frozen strings
mà nó còn có cách sử dụng khác nhau. Hơn nữa sự khác nhau là symbol đại diện cho 1 object nó không thay đổi. Còn với mỗi sting nó là 1 object khác nhau.
[33] pry(main)> a = :title
=> :title
[34] pry(main)> b = :title
=> :title
[35] pry(main)> a.object_id
=> 4194588
[36] pry(main)> b.object_id
=> 4194588
[37] pry(main)>
[37] pry(main)> c = "abc"
=> "abc"
[38] pry(main)> d = "abc"
=> "abc"
[39] pry(main)> c.object_id
=> 63790820
[40] pry(main)> d.object_id
=> 63503460
Nhìn vào trên ta có thể thấy được object_id của a
và b
là không thay đổi trong khi đó c
và d
là 2 object khác nhau (chỉ có chung giá trị mà thôi).
Khi nào thì sử dụng Symbol
Một trong những cách sử dụng phổ biến nhất của symbol là đại diện cho method
và tên của instance variable
.
attr_reader :title
Ở đây, :title
sau attr_reader
là một symbol đại diện cho instance variable @title
Symbol cũng được sử dụng như một key của Hash
hash = {a: 1, b: 2, c: 3}
Chúng ta hay sử dụng symbol như key của hash vì symbol trông đẹp hơn, chúng không thay đổi và nếu bạn dùng benchmark để so sánh strings key và symbol key bạn sẽ thấy string key sẽ chậm hơn khoảng 1.21 (phiên bản 2.2.7) lần so với symbol key
2.2.7 :053 > require 'benchmark/ips'
=> false
2.2.7 :054 >
2.2.7 :055 > str = { "foo" => 1 }
=> {"foo"=>1}
2.2.7 :056 > sym = { foo: 1 }
=> {:foo=>1}
2.2.7 :057 >
2.2.7 :058 > Benchmark.ips do |x|
2.2.7 :059 > x.report("string") { str["foo"] }
2.2.7 :060?> x.report("symbol") { sym[:foo] }
2.2.7 :061?>
2.2.7 :062 > x.compare!
2.2.7 :063?> end
Warming up --------------------------------------
string 179.353k i/100ms
symbol 195.408k i/100ms
Calculating -------------------------------------
string 5.966M (± 2.5%) i/s - 29.952M in 5.024160s
symbol 7.238M (± 2.1%) i/s - 36.346M in 5.023921s
Comparison:
symbol: 7237750.5 i/s
string: 5965969.0 i/s - 1.21x slower
Symbol cũng có thể sử dụng trong metaprograming method like send
2.2.7 :077 > [1,2,3].send(:first)
=> 1
- Bạn nên sử dụng
symbols
như tên hay labels cho mọi thứ (như các methods, key của hash..) và sử dụngstrings
khi bạn quan tâm nhiều hơn về dữ liệu.
Convert giữa Strings và Symbols
Thi thoảng bạn sẽ nhận được một symbol từ một methods nào đó và bạn cần phải convert nó sang string để có thể so sánh với string khác. Hoặc có thể dùng các methods khác của String.
Ví dụ, ta có các instance methods của Stings
và muốn lọc ra những methods kết thúc bằng dấu ?
[2] pry(main)> String.instance_methods
=> [:<=>,
:==,
:===,
:eql?,
:hash,
:casecmp,
:+,
:*,
:%,
:[],
:[]=,
:insert,
:length,
:size,
:bytesize,
:empty?,
:=~,
...
]
Trên trả về một list symbols, những symbols này đại diện cho tên của methods.
[3] pry(main)> :empty?[-1]
=> "?"
[4] pry(main)> :empty?.end_with?("?")
NoMethodError: undefined method `end_with?' for :empty?:Symbol
from (pry):4:in `<main>'
=> end_with
là 1 method của Strings vì vậy chúng ta không thể dùng nó. => chúng ta cần phải convert symbol qua string. Và method to_s
giúp chúng ta làm việc đó.
[7] pry(main)> :empty?.to_s.end_with?("?")
=> true
[10] pry(main)> String.instance_methods.map(&:to_s).select{|s| s.end_with?("?")}
=> ["eql?",
"empty?",
"include?",
"start_with?",
"end_with?",
"valid_encoding?",
"ascii_only?",
"unicode_normalized?",
"blank?",
"is_utf8?",
"starts_with?",
"ends_with?",
"acts_like_string?",
"exclude?",
"not_ascii_only?",
"valid_email?",
"between?",
"present?",
"acts_like?",
"duplicable?",
"in?",
"html_safe?",
"is_haml?",
"nil?",
"tainted?",
"untrusted?",
"frozen?",
"instance_variable_defined?",
"instance_of?",
"kind_of?",
"is_a?",
"respond_to?",
"equal?"]
Và ngược lại chúng ta sẽ dùng method to_sym
để convert từ string qua symbol
[11] pry(main)> "empty?".to_sym
=> :empty?
Khởi tại một mảng các symbols và strings
[12] pry(main)> %i(a b c)
=> [:a, :b, :c]
[13] pry(main)> %w(a b c)
=> ["a", "b", "c"]
Chú ý (về GC)
Một điều đáng chú ý về symbol nữa là. đối với phiên bản ruby 2.2 trở về trước thì symbols
sẽ không được tự động removed sau một thời gian không dùng đến nữa, không như strings hoặc hashes,.. Những từ phiên bản 2.2 trở về sau thì nó đã được tự động removed sau một thời gian dài không dùng đến nữa.
Có một số symbols sẽ không bao giờ bị xóa khỏi bộ nhớ, những symbol đó được gọi là immortal symbols
(không được thu thập bởi GC). Với những symbol được tạo bằng cách gọi trực tiếp như là :title
sẽ tự động trở thành immortal symbols
còn đối với những string được convert sang symbol thì nó sẽ trở thành mortal_dynamic_symbol
(sẽ được thu thập bởi GC và xóa đi sau một thời gian nó không được dùng nữa).
All rights reserved