Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Hiểu về Symbol trong Ruby: Định nghĩa, Sử dụng và Khác biệt chính

Một symbol trông như thế này:

:testing

Một số người nhầm lẫn symbol với biến, nhưng chúng không liên quan đến biến…

…symbol giống chuỗi (string) hơn nhiều.

Vậy khác biệt giữa Ruby symbol & string là gì?

String được dùng để làm việc với dữ liệu (data).

Symbol là định danh (identifier).

Đó là khác biệt chính:

Symbol không chỉ là một loại chuỗi khác, chúng có mục đích khác nhau.

Khi nào dùng Symbol

Một trong những trường hợp phổ biến nhất cho symbol là đại diện cho tên phương thức & tên biến instance.

Ví dụ:

attr_reader :title

:title sau attr_reader là một symbol đại diện cho biến instance @title.

Bạn cũng có thể dùng symbol làm key cho hash.

Ví dụ:

hash = {a: 1, b: 2, c: 3}

Lợi ích?

Symbol trông đẹp hơn, chúng immutable (bất biến) & nếu bạn benchmark string keys vs symbol keys bạn sẽ thấy string keys chậm hơn khoảng 1.70 lần.

Bằng immutable tôi muốn nói mỗi symbol là duy nhất, và không thể thay đổi:

:rubyguides.object_id # 437768
:rubyguides.object_id # 437768

Hai symbol này là cùng một đối tượng vì chúng có cùng object id.

Symbol có thể dùng trong phương thức metaprogramming như send:

[1,2,3].send(:first)

Tóm lại:

Bạn nên dùng symbol như tên hoặc nhãn cho các thứ (như phương thức) & dùng string khi bạn quan tâm hơn về dữ liệu (các ký tự riêng lẻ).

Chuyển đổi giữa String & Symbol

Bạn có thể chuyển symbol thành string khi cần thiết.

Tại sao bạn muốn làm vậy?

Vì symbol chỉ có một tập hợp con các phương thức mà String có.

Và symbol không thể thay đổi, nên nếu bạn muốn làm việc với các ký tự riêng lẻ của symbol thì bạn muốn string.

Bạn có thể làm điều này dùng phương thức to_s.

Ví dụ, khi dùng method_missing bạn nhận tên phương thức thiếu dưới dạng symbol. Bạn có thể muốn kiểm tra xem tên phương thức này có khớp với mẫu nhất định không (như kết thúc bằng ?).

Ví dụ:

def method_missing(method_name, *args, &block)
  if method_name.to_s[-1] == "?"
    # làm gì đó
  else
    super
  end
end

Bạn cũng có thể chuyển đối tượng string thành đối tượng symbol.

Phương thức để làm điều này là String#to_sym:

"rubyguides".to_sym # :rubyguides

Tạo Mảng Symbol

Nếu bạn muốn tạo mảng symbol bạn có thể dùng mã này:

symbols = %i(a b c) # [:a, :b, :c]

Điều này giúp bạn không phải gõ dấu hai chấm & dấu phẩy.

Tương tự phiên bản string %w:

strings = %w(a b c) # ["a", "b", "c"]

Video về Ruby Symbols

Symbol GC (Nâng cao)

Một sự thật thú vị khác về symbol là có các loại khác nhau.

Lý do là symbol không được garbage collected trước Ruby 2.2, có nghĩa là chúng không được dọn dẹp khỏi bộ nhớ khi không còn cần thiết như các đối tượng Ruby thông thường (string, hash, mảng…).

Bạn có thể thấy ví dụ ở đây:

p Symbol.all_symbols.size # 2443
('aa'..'aj').map(&:to_sym)
GC.start
p Symbol.all_symbols.size # 2453

Bạn sẽ thấy tổng số symbol tăng 10, đúng như mong đợi vì chúng ta tạo 10 symbol mới.

Nhưng kể từ Ruby 2.2 các symbol này bị xóa khỏi bộ nhớ vì chúng chỉ tạm thời & không được dùng bởi cái khác trong mã này.

Nếu bạn chạy mã này trên phiên bản Ruby có Symbol GC bật thì cả hai số lượng symbol sẽ giống nhau.

Một số symbol sẽ không bao giờ bị xóa khỏi bộ nhớ, chúng được gọi là “immortal symbols” (symbol bất tử).

Bạn có thể đếm chúng dùng module ObjectSpace:

require 'objspace'
ObjectSpace.count_symbols
# {:mortal_dynamic_symbol=>3, :immortal_dynamic_symbol=>5, :immortal_static_symbol=>3663, :immortal_symbol=>3668}

Lưu ý symbol tạo trực tiếp, như :a1 sẽ tự động trở thành immortal symbol. Tạo phương thức mới cũng sẽ tạo immortal_static_symbol đi kèm.

Vậy mortal symbol đến từ đâu?

Từ string chuyển thành symbol với phương thức to_sym.

Bạn có thể kiểm tra điều này dùng ObjectSpace.count_symbols.

Và nếu bạn thắc mắc immortal_dynamic_symbol là gì, đó là symbol đã được thăng cấp từ mortal sang immortal. Điều này có thể xảy ra khi bạn tạo phương thức với tên của mortal symbol.

Tóm tắt

Trong bài viết này bạn đã học:

  • Symbol là immutable (bất biến)
  • Symbol không phải con trỏ đến giá trị, chúng là giá trị chính nó
  • String dành cho dữ liệu & symbol dành cho danh tính (identity)
  • Cách chuyển đổi giữa string & symbol
  • Symbol GC được giới thiệu trong Ruby 2.2 để dọn dẹp symbol tạm thời

Hy vọng bạn học được điều gì đó mới!

Vui lòng chia sẻ bài viết này để nó tiếp cận nhiều người hơn 🙂