Sự khác nhau giữa Symbols và Strings
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ó 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ụng strings 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).