Kapan menggunakan simbol sebagai ganti string di Ruby?

98

Jika ada setidaknya dua contoh dari string yang sama dalam skrip saya, haruskah saya menggunakan simbol?

Alan Coromano
sumber

Jawaban:

175

TL; DR

Aturan praktis yang sederhana adalah menggunakan simbol setiap kali Anda membutuhkan pengenal internal. Untuk Ruby <2.2 hanya gunakan simbol jika tidak dibuat secara dinamis, untuk menghindari kebocoran memori.

Jawaban lengkap

Satu-satunya alasan untuk tidak menggunakannya untuk pengenal yang dibuat secara dinamis adalah karena masalah memori.

Pertanyaan ini sangat umum karena banyak bahasa pemrograman tidak memiliki simbol, hanya string, dan dengan demikian string juga digunakan sebagai pengenal dalam kode Anda. Anda harus mengkhawatirkan tentang simbol-simbol apa yang dimaksudkan untuk menjadi , tidak hanya ketika Anda harus menggunakan simbol-simbol . Simbol dimaksudkan sebagai pengenal. Jika Anda mengikuti filosofi ini, kemungkinan besar Anda akan melakukan hal-hal dengan benar.

Ada beberapa perbedaan antara penerapan simbol dan string. Hal terpenting tentang simbol adalah bahwa simbol itu tidak dapat diubah . Ini berarti bahwa nilai mereka tidak akan pernah berubah. Karenanya, simbol dibuat lebih cepat daripada string dan beberapa operasi seperti membandingkan dua simbol juga lebih cepat.

Fakta bahwa sebuah simbol tidak dapat diubah memungkinkan Ruby untuk menggunakan objek yang sama setiap kali Anda mereferensikan simbol tersebut, sehingga menghemat memori. Jadi setiap kali interpreter membacanya, interpreter :my_keydapat mengambilnya dari memori alih-alih membuat instance lagi. Ini lebih murah daripada menginisialisasi string baru setiap saat.

Anda bisa mendapatkan daftar semua simbol yang sudah dipakai dengan perintah Symbol.all_symbols:

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

Untuk versi Ruby sebelum 2.2, setelah simbol dibuat, memori ini tidak akan pernah bisa bebas lagi . Satu-satunya cara untuk mengosongkan memori adalah dengan memulai ulang aplikasi. Jadi simbol juga menjadi penyebab utama kebocoran memori saat digunakan secara tidak benar. Cara termudah untuk menghasilkan kebocoran memori adalah dengan menggunakan metode to_sympada data input pengguna, karena data ini akan selalu berubah, sebagian memori baru akan digunakan selamanya dalam contoh perangkat lunak. Ruby 2.2 memperkenalkan simbol pengumpul sampah , yang membebaskan simbol yang dibuat secara dinamis, sehingga kebocoran memori yang dihasilkan dengan membuat simbol secara dinamis tidak lagi menjadi masalah.

Menjawab pertanyaan Anda:

Apakah benar saya harus menggunakan simbol, bukan string jika ada setidaknya dua string yang sama dalam aplikasi atau skrip saya?

Jika yang Anda cari adalah pengenal untuk digunakan secara internal di kode Anda, Anda harus menggunakan simbol. Jika Anda mencetak keluaran, Anda harus menggunakan string, meskipun muncul lebih dari sekali, bahkan mengalokasikan dua objek berbeda dalam memori.

Berikut alasannya:

  1. Mencetak simbol akan lebih lambat daripada mencetak string karena dilemparkan ke string.
  2. Memiliki banyak simbol yang berbeda akan meningkatkan penggunaan memori aplikasi Anda secara keseluruhan karena tidak pernah dialokasikan. Dan Anda tidak pernah menggunakan semua string dari kode Anda pada saat yang bersamaan.

Kasus penggunaan oleh @AlanDert

@AlanDert: jika saya sering menggunakan sesuatu seperti% input {type:: checkbox} dalam kode haml, apa yang harus saya gunakan sebagai kotak centang?

Saya iya.

@AlanDert: Tetapi untuk mencetak simbol pada halaman html, itu harus diubah menjadi string, bukan? lalu apa gunanya menggunakannya?

Apa jenis inputnya? Pengenal jenis masukan yang ingin Anda gunakan atau sesuatu yang ingin Anda tunjukkan kepada pengguna?

Memang benar bahwa ini akan menjadi kode HTML di beberapa titik, tetapi pada saat Anda menulis baris kode Anda, itu dimaksudkan sebagai pengenal - ini mengidentifikasi jenis bidang input yang Anda butuhkan. Oleh karena itu, ini digunakan berulang kali dalam kode Anda, dan selalu memiliki "string" karakter yang sama dengan pengenal dan tidak akan menghasilkan kebocoran memori.

Karena itu, mengapa kita tidak mengevaluasi data untuk melihat apakah string lebih cepat?

Ini adalah patokan sederhana yang saya buat untuk ini:

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: "checkbox"}').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: :checkbox}').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

Tiga keluaran:

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

Jadi menggunakan smbol sebenarnya sedikit lebih cepat daripada menggunakan string. Mengapa demikian? Itu tergantung pada cara HAML diimplementasikan. Saya perlu meretas sedikit kode HAML untuk melihatnya, tetapi jika Anda tetap menggunakan simbol dalam konsep pengenal, aplikasi Anda akan lebih cepat dan dapat diandalkan. Saat pertanyaan muncul, ukur dan dapatkan jawaban Anda.

fotanus.dll
sumber
@andrewcockerham Tautan yang Anda berikan tidak berfungsi (Kesalahan-404). Anda harus menghapus yang terakhir /(setelah strings) dari tautan. Ini dia: www.reactive.io/tips/2009/01/11/the-difference-between-ruby-‌ symbol-and-string
Atul Khanduri
14

Sederhananya, simbol adalah nama, terdiri dari karakter, tetapi tidak berubah. Sebaliknya, string adalah penampung yang dipesan untuk karakter, yang isinya boleh diubah.

Boris Stitnicky
sumber
4
+1. Simbol dan String adalah hal yang sangat berbeda. Sebenarnya tidak ada kebingungan tentang mana yang akan digunakan, kecuali mereka telah diajarkan dengan buruk (yaitu kekeliruan "sebuah simbol hanyalah string yang tidak dapat diubah").
Jörg W Mittag
@ JörgWMittag: Tepat.
Boris Stitnicky
5
Anda benar, tapi jangan menjawab pertanyaan yang dibuat. OP mengacaukan string dengan simbol, tidak cukup mengatakan itu adalah hal yang berbeda - Anda harus membantunya untuk memahami apa
persamaan
1
@ JörgWMittag yang terjadi di seluruh web tampaknya, kecuali jika Anda melihat ke dalam dokumentasi atau cukup beruntung untuk menemukan orang yang peduli untuk menjelaskan hal-hal sebagaimana adanya.
sargas
8
  1. Simbol Ruby adalah objek dengan perbandingan O (1)

Untuk membandingkan dua string, kita perlu melihat setiap karakter. Untuk dua string dengan panjang N, ini akan membutuhkan perbandingan N + 1 (yang oleh para ilmuwan komputer disebut sebagai "waktu O (N)").

def string_comp str1, str2
  return false if str1.length != str2.length
  for i in 0...str1.length
    return false if str1[i] != str2[i]
  end
  return true
end
string_comp "foo", "foo"

Tetapi karena setiap kemunculan: foo mengacu pada objek yang sama, kita dapat membandingkan simbol dengan melihat ID objek. Kita dapat melakukan ini dengan satu perbandingan (yang oleh para ilmuwan komputer disebut sebagai "O (1) waktu").

def symbol_comp sym1, sym2
  sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
  1. Simbol Ruby adalah label dalam enumerasi bentuk bebas

Di C ++, kita dapat menggunakan "enumerasi" untuk mewakili keluarga konstanta terkait:

enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status  = CLOSED;

Tetapi karena Ruby adalah bahasa yang dinamis, kami tidak khawatir tentang mendeklarasikan tipe BugStatus, atau melacak nilai-nilai legal. Sebagai gantinya, kami mewakili nilai pencacahan sebagai simbol:

original_status = :open
current_status  = :closed

3. Simbol Ruby adalah nama yang konstan dan unik

Di Ruby, kita dapat mengubah konten string:

"foo"[0] = ?b # "boo"

Tapi kita tidak bisa mengubah isi simbol:

:foo[0]  = ?b # Raises an error
  1. Simbol Ruby adalah kata kunci untuk argumen kata kunci

Saat meneruskan argumen kata kunci ke fungsi Ruby, kami menentukan kata kunci menggunakan simbol:

# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
        :action => 'show',
        :id => bug.id
  1. Simbol Ruby adalah pilihan tepat untuk kunci hash

Biasanya, kami akan menggunakan simbol untuk mewakili kunci tabel hash:

options = {}
options[:auto_save]     = true
options[:show_comments] = false
Arun Kumar M
sumber
5

Berikut adalah patokan string vs simbol bagus yang saya temukan di codecademy:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  1000_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

Outputnya adalah:

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
Yurii
sumber
2
Mari kita tidak melupakan fakta bahwa ini adalah sepersepuluh detik.
Casey
Semuanya relatif. Terkadang masalah keseratus.
Yurii
2
Seperseratus detik lebih dari satu juta iterasi? Jika itu pengoptimalan terbaik yang tersedia untuk Anda, program Anda sudah dioptimalkan dengan cukup baik, saya kira.
Casey
0
  • gunakan simbol sebagai pengenal kunci hash

    {key: "value"}

  • simbol memungkinkan Anda memanggil metode dalam urutan yang berbeda

     def write (file :, data :, mode: "ascii")
          # dihapus agar singkat
     akhir
     tulis (data: 123, file: "test.txt")
  • membekukan untuk disimpan sebagai string dan menghemat memori

    label = 'My Label'.freeze

Oshan Wisumperuma
sumber