Cách hiểu các ký hiệu trong Ruby


85

Mặc dù đã đọc " Tìm hiểu các ký hiệu Ruby ", tôi vẫn bối rối bởi cách biểu diễn dữ liệu trong bộ nhớ khi sử dụng các ký hiệu. Nếu một biểu tượng, hai trong số chúng được chứa trong các đối tượng khác nhau, tồn tại ở cùng một vị trí bộ nhớ, thì làm thế nào để chúng chứa các giá trị khác nhau ? Tôi đã mong đợi cùng một vị trí bộ nhớ chứa cùng một giá trị.

Đây là một trích dẫn từ liên kết:

Không giống như chuỗi, các ký hiệu cùng tên được khởi tạo và tồn tại trong bộ nhớ chỉ một lần trong một phiên của ruby

Tôi không hiểu cách nó quản lý để phân biệt các giá trị có trong cùng một vị trí bộ nhớ.

Hãy xem xét ví dụ này:

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1patient2cả hai đều là băm, điều đó tốt. :rubytuy nhiên là một biểu tượng. Nếu chúng ta phải xuất như sau:

patient1.each_key {|key| puts key.to_s}

Sau đó, những gì sẽ được đầu ra? "red", hoặc "programming"?

Quên hàm băm trong một giây, tôi nghĩ rằng một biểu tượng là một con trỏ đến một giá trị. Các câu hỏi tôi có là:

  • Tôi có thể gán giá trị cho một biểu tượng không?
  • Có phải một biểu tượng chỉ là một con trỏ đến một biến có giá trị trong đó không?
  • Nếu các biểu tượng là toàn cầu, điều đó có nghĩa là một biểu tượng luôn hướng đến một thứ?

1
Nó sẽ xuất ra ": ruby", vì bạn đang in một Biểu tượng. Nếu bạn nói puts patient1[:ruby], nó sẽ in "màu đỏ", nếu bạn nói puts patient2[:ruby], nó sẽ in "lập trình".
ROSS

1
Một biểu tượng KHÔNG phải là một con trỏ đến một giá trị. Bên trong một biểu tượng chỉ là một số nguyên.
akuhn

Câu trả lời:


62

Xem xét điều này:

x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true

x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false

Vì vậy, dù bạn tạo một đối tượng biểu tượng, miễn là nội dung của nó giống nhau, nó sẽ tham chiếu đến cùng một đối tượng trong bộ nhớ. Đây không phải là một vấn đề bởi vì một biểu tượng là một đối tượng bất biến . Các chuỗi có thể thay đổi.


(Trả lời bình luận bên dưới)

Trong bài viết gốc, giá trị không được lưu trữ trong một biểu tượng, nó đang được lưu trữ trong một hàm băm. Xem xét điều này:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

Điều này tạo ra sáu đối tượng trong bộ nhớ - bốn đối tượng chuỗi và hai đối tượng băm.

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

Điều này chỉ tạo ra năm đối tượng trong bộ nhớ - một biểu tượng, hai chuỗi và hai đối tượng băm.


Tuy nhiên, ví dụ trong liên kết cho thấy các ký hiệu chứa các giá trị khác nhau , nhưng ký hiệu có cùng tên và cùng vị trí bộ nhớ. Khi chúng xuất ra, chúng có các giá trị khác nhau , đó là phần tôi không nhận được. Chắc chắn chúng phải chứa cùng một giá trị?
Kezzer

1
Tôi vừa thực hiện một chỉnh sửa để thử và giải thích cách tôi vẫn còn bối rối. Bộ não của tôi không thể tính toán;)
Kezzer

48
Các biểu tượng không chứa giá trị, chúng giá trị. Hàm băm chứa các giá trị.
Mladen Jablanović

5
Đó là Hash(được tạo bởi {... => ...} trong mã của bạn) lưu trữ các cặp khóa / giá trị, không phải Symbolchính chúng. Các Symbols (ví dụ :symbolhoặc :symhoặc :ruby) là các phím trong các cặp. Chỉ là một phần của Hashhọ, họ "trỏ" đến bất cứ điều gì.
James A. Rosen

1
Biểu tượng đang được sử dụng làm khóa trong băm không phải là giá trị, đó là lý do tại sao chúng có thể khác nhau, tương tự như việc sử dụng key1 = 'ruby' và hash1 = {key1 => 'value' ...} hash2 = { key1 => 'value2' ...}.
Joshua Olson

53

Tôi đã có thể tìm kiếm các biểu tượng khi tôi nghĩ về nó như thế này. Chuỗi Ruby là một đối tượng có một loạt các phương thức và thuộc tính. Mọi người thích sử dụng chuỗi cho khóa và khi chuỗi được sử dụng cho khóa thì tất cả các phương thức bổ sung đó sẽ không được sử dụng. Vì vậy, họ đã tạo ra các biểu tượng, là các đối tượng chuỗi với tất cả các chức năng bị loại bỏ, ngoại trừ thứ cần thiết để nó trở thành một khóa tốt.

Chỉ cần nghĩ về các biểu tượng như một chuỗi không đổi.


2
Đọc qua các bài viết, điều này có lẽ có ý nghĩa nhất đối với tôi. : ruby ​​chỉ được lưu ở đâu đó trong bộ nhớ, nếu mình dùng "ruby" ở đâu đó rồi lại "ruby" ở đâu đó thì chỉ là sự trùng lặp thôi. Vì vậy sử dụng các ký hiệu là một cách để giảm sự trùng lặp của dữ liệu chung. Như bạn nói, chuỗi không đổi. Tôi đoán có một số cơ chế cơ bản sẽ tìm lại biểu tượng đó để sử dụng?
Kezzer

@Kezzer Câu trả lời này thực sự hay và có vẻ đúng với tôi, nhưng nhận xét của bạn nói điều gì đó khác và sai hoặc gây hiểu lầm, nhận xét của bạn nói về sự trùng lặp dữ liệu với các chuỗi và đó là lý do cho các ký hiệu, đó là sai hoặc gây hiểu nhầm. biểu tượng nhiều lần sẽ không sử dụng thêm dung lượng bộ nhớ, nhưng bạn có thể có điều đó cho các chuỗi trong nhiều ngôn ngữ, ví dụ như một số ngôn ngữ lập trình nếu bạn viết "abc" và ở chỗ khác "abc", trình biên dịch sẽ thấy đó là chuỗi giá trị giống nhau và lưu trữ nó ở cùng một nơi làm cho nó trở thành một đối tượng giống nhau, đó được gọi là xâu chuỗi và c # thực hiện điều đó.
barlop

Vì vậy, về cơ bản nó là một phiên bản cực kỳ nhẹ của một chuỗi?
stevec

34

Biểu tượng :rubykhông chứa "red"hoặc "programming". Biểu tượng :rubychỉ là biểu tượng :ruby. Đó là các hàm băm của bạn patient1patient2mỗi hàm chứa các giá trị đó, trong mỗi trường hợp được trỏ tới bởi cùng một khóa.

Hãy nghĩ về nó theo cách này: Nếu bạn đi vào phòng khách vào buổi sáng Giáng sinh và thấy hai chiếc hộp có gắn thẻ ghi "Kezzer" trên chúng. Trên có tất trong đó, và kia có than. Bạn sẽ không bị nhầm lẫn và hỏi làm thế nào "Kezzer" có thể chứa cả tất và than, mặc dù nó cùng tên. Bởi vì tên không chứa quà (crappy). Nó chỉ đang chỉ vào họ. Tương tự, :rubykhông chứa các giá trị trong hàm băm của bạn, nó chỉ trỏ vào chúng.


2
Câu trả lời này hoàn toàn có ý nghĩa.
Vass

Điều này nghe có vẻ giống như một sự kết hợp hoàn toàn giữa các hàm băm và các ký hiệu. Một biểu tượng không trỏ đến một giá trị, nếu bạn muốn nói nó xuất hiện khi ở trong một hàm băm, thì điều đó có thể được tranh luận, nhưng một biểu tượng không nhất thiết phải ở trong một hàm băm. Bạn có thể nói rằng mystring = :steveT biểu tượng không chỉ bất cứ thứ gì. Khóa trong hàm băm có một giá trị được liên kết và khóa có thể là một ký hiệu. Nhưng một biểu tượng không nhất thiết phải có trong hàm băm.
barlop

27

Bạn có thể giả định rằng khai báo bạn đã thực hiện xác định giá trị của Biểu tượng là một thứ khác với giá trị của nó. Trên thực tế, một Biểu tượng chỉ là một giá trị Chuỗi được "nội bộ hóa" không đổi. Chính vì chúng được lưu trữ bằng cách sử dụng một mã định danh số nguyên đơn giản nên chúng thường được sử dụng vì hiệu quả hơn việc quản lý một số lượng lớn các chuỗi có độ dài thay đổi.

Lấy ví dụ trường hợp của bạn:

patient1 = { :ruby => "red" }

Điều này sẽ được đọc là: "khai báo một biến bệnh nhân1 và xác định nó là một Hash, và trong cửa hàng này giá trị 'màu đỏ' dưới khóa (biểu tượng 'ruby')"

Một cách viết khác là:

patient1 = Hash.new
patient1[:ruby] = 'red'

puts patient1[:ruby]
# 'red'

Khi bạn đang thực hiện một nhiệm vụ, hầu như không có gì ngạc nhiên khi kết quả bạn nhận lại giống hệt với những gì bạn đã giao ngay từ đầu.

Khái niệm Biểu tượng có thể hơi khó hiểu vì nó không phải là một tính năng của hầu hết các ngôn ngữ khác.

Mỗi đối tượng Chuỗi là khác biệt ngay cả khi các giá trị giống hệt nhau:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860

Mọi Biểu tượng có cùng giá trị đề cập đến cùng một đối tượng:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

Chuyển đổi chuỗi thành biểu tượng ánh xạ các giá trị giống hệt nhau thành cùng một Biểu tượng duy nhất:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  v = v.to_sym
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

Tương tự như vậy, việc chuyển đổi từ Biểu tượng sang Chuỗi sẽ tạo ra một chuỗi riêng biệt mỗi lần:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  v = v.to_s
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220

Bạn có thể coi các giá trị Biểu tượng như được rút ra từ bảng Hash bên trong và bạn có thể xem tất cả các giá trị đã được mã hóa thành Biểu tượng bằng cách gọi phương thức đơn giản:

Symbol.all_values

# => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...

Khi bạn xác định các ký hiệu mới bằng ký hiệu dấu hai chấm hoặc bằng cách sử dụng .to_sym, bảng này sẽ phát triển.


17

Biểu tượng không phải là con trỏ. Chúng không chứa giá trị. Các biểu tượng chỉ đơn giản . :rubylà biểu tượng :rubyvà đó là tất cả những gì liên quan đến nó. Nó không chứa một giá trị, nó không làm gì cả, nó chỉ tồn tại dưới dạng biểu tượng :ruby. Biểu tượng :rubylà một giá trị giống như số 1. Nó không trỏ đến giá trị khác nhiều hơn số 1.


13
patient1.each_key {|key| puts key.to_s}

Sau đó, những gì sẽ được đầu ra? "đỏ", hay "lập trình"?

Không, nó sẽ xuất ra "ruby".

Bạn đang nhầm lẫn các ký hiệu và hàm băm. Chúng không liên quan, nhưng chúng hữu ích với nhau. Biểu tượng được đề cập là :ruby; nó không liên quan gì đến các giá trị trong hàm băm và biểu diễn số nguyên bên trong của nó sẽ luôn giống nhau và "giá trị" (khi được chuyển đổi thành chuỗi) sẽ luôn là "ruby".


10

Nói ngắn gọn

Các biểu tượng giải quyết vấn đề tạo ra các biểu diễn bất biến, có thể đọc được của con người cũng có lợi ích là thời gian chạy đơn giản hơn để tra cứu so với chuỗi. Hãy nghĩ về nó như một cái tên hoặc nhãn có thể được sử dụng lại.

Tại sao: màu đỏ tốt hơn "màu đỏ"

Trong ngôn ngữ hướng đối tượng động, bạn tạo cấu trúc dữ liệu lồng nhau, phức tạp với các tham chiếu có thể đọc được. Hàm băm là một trường hợp sử dụng phổ biến trong đó bạn ánh xạ các giá trị tới các khóa duy nhất - ít nhất là duy nhất cho mỗi phiên bản. Bạn không thể có nhiều hơn một khóa "đỏ" cho mỗi hàm băm.

Tuy nhiên, bộ xử lý sẽ hiệu quả hơn nếu sử dụng chỉ mục số thay vì các khóa chuỗi. Vì vậy, các ký hiệu đã được giới thiệu như một sự thỏa hiệp giữa tốc độ và khả năng đọc. Các ký hiệu giải quyết dễ dàng hơn nhiều so với chuỗi tương đương. Bằng cách con người có thể đọc được và dễ dàng cho thời gian chạy để phân giải các ký hiệu là một bổ sung lý tưởng cho một ngôn ngữ động.

Những lợi ích

Vì các biểu tượng là bất biến nên chúng có thể được chia sẻ trong suốt thời gian chạy. Nếu hai trường hợp băm có nhu cầu chung về từ vựng hoặc ngữ nghĩa cho một mục màu đỏ thì biểu tượng: red sẽ sử dụng khoảng một nửa bộ nhớ mà chuỗi "red" sẽ cần cho hai hàm băm.

Vì: red luôn phân giải trở lại cùng một vị trí trong bộ nhớ, nó có thể được sử dụng lại trong hàng trăm phiên bản băm mà hầu như không tăng bộ nhớ, trong khi sử dụng "red" sẽ thêm chi phí bộ nhớ vì mỗi phiên bản băm sẽ cần lưu trữ chuỗi có thể thay đổi. sự sáng tạo.

Không chắc Ruby thực sự triển khai các biểu tượng / chuỗi như thế nào nhưng rõ ràng một biểu tượng cung cấp ít chi phí triển khai hơn trong thời gian chạy vì nó là một biểu diễn cố định. Các ký hiệu cộng thêm cần ít ký tự hơn để nhập so với một chuỗi được trích dẫn và ít nhập hơn là mục tiêu theo đuổi vĩnh viễn của các Rubyist thực thụ.

Tóm lược

Với một ký hiệu như: red, bạn có thể đọc được biểu diễn chuỗi với chi phí ít hơn do chi phí của các hoạt động so sánh chuỗi và nhu cầu lưu trữ từng cá thể chuỗi trong bộ nhớ.


4

Tôi khuyên bạn nên đọc bài viết trên Wikipedia về bảng băm - tôi nghĩ nó sẽ giúp bạn hiểu ý nghĩa {:ruby => "red"}thực sự của nó.

Một bài tập khác có thể giúp bạn hiểu tình huống: hãy xem xét {1 => "red"}. Về mặt ngữ nghĩa, điều này không có nghĩa là "đặt giá trị 1thành "red"", điều này là không thể trong Ruby. Thay vào đó, nó có nghĩa là "tạo một đối tượng Hash và lưu trữ giá trị "red"cho khóa 1.


3
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1patient2cả hai đều là băm, điều đó tốt. :rubytuy nhiên là một biểu tượng. Nếu chúng tôi xuất ra như sau:

patient1.each_key {|key| puts key.to_s}

Sau đó, những gì sẽ được đầu ra? "đỏ", hay "lập trình"?

Tất nhiên là không. Đầu ra sẽ là ruby. Điều này, BTW, bạn có thể phát hiện ra trong thời gian ngắn hơn thời gian bạn phải nhập câu hỏi, bằng cách chỉ cần nhập câu hỏi đó vào IRB.

Tại sao nó sẽredhoặc programming? Các biểu tượng luôn tự đánh giá. Giá trị của biểu tượng :ruby:rubychính ký hiệu và biểu diễn chuỗi của biểu tượng :rubylà giá trị chuỗi "ruby".

[BTW: putsluôn chuyển đổi các đối số của nó thành chuỗi. Không cần thiết phải gọi to_snó.]


Tôi không có IRB trên máy hiện tại, tôi cũng không thể cài đặt nó, vì vậy tại sao, tôi xin lỗi vì điều đó.
Kezzer

2
@Kezzer: Đừng lo lắng, tôi chỉ tò mò. Đôi khi bạn vùi mình sâu vào một vấn đề đến nỗi bạn không thể nhìn thấy những điều đơn giản nhất nữa. Về cơ bản, khi tôi cắt và dán câu hỏi của bạn vào IRB, tôi chỉ tự hỏi: "tại sao anh ấy không tự mình làm điều đó?" Và đừng lo lắng, bạn không phải là người đầu tiên (cũng như bạn sẽ không phải là người cuối cùng) hỏi "cái này in ra cái gì" khi câu trả lời là "chỉ cần chạy nó!" BTW: đây là IRB tức thì của bạn, mọi lúc, mọi nơi, không cần cài đặt: TryRuby.Org Hoặc Ruby-Versions.Net cấp cho bạn quyền truy cập SSH vào tất cả các phiên bản MRI từng được phát hành + YARV + JRuby + Rubinius + REE.
Jörg W Mittag

Cảm ơn, bây giờ chỉ chơi với nó thôi. Tôi vẫn còn một chút bối rối mặc dù vậy hãy xem lại nó một lần nữa.
Kezzer

0

Tôi mới sử dụng Ruby, nhưng tôi nghĩ (hy vọng?) Đây là một cách đơn giản để xem xét nó ...

Một biểu tượng không phải là một biến hoặc một hằng số. Nó không đại diện cho hoặc trỏ đến một giá trị. Biểu tượng LÀ một giá trị.

Tất cả chỉ là một chuỗi không có đối tượng trên đầu. Văn bản và chỉ văn bản.

Vì vậy, điều này:

"hellobuddy"

Giống như thế này:

:hellobuddy

Ví dụ: ngoại trừ bạn không thể làm: hellobuddy.upcase. Đó là giá trị chuỗi và CHỈ là giá trị chuỗi.

Tương tự như vậy, điều này:

greeting =>"hellobuddy"

Giống như thế này:

greeting => :hellobuddy

Nhưng, một lần nữa, không có chi phí đối tượng chuỗi.


-1

Một cách dễ dàng để bạn giải quyết vấn đề này là nghĩ, "điều gì sẽ xảy ra nếu tôi đang sử dụng một chuỗi chứ không phải một biểu tượng?

patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }

Nó không khó hiểu chút nào, phải không? Bạn đang sử dụng "ruby" làm khóa trong hàm băm .

"ruby"là một chuỗi ký tự, vì vậy đó là giá trị. Địa chỉ bộ nhớ, hoặc con trỏ, không có sẵn cho bạn. Mỗi khi bạn gọi "ruby", bạn đang tạo một thể hiện mới của nó, tức là, tạo một ô nhớ mới chứa cùng giá trị - "ruby".

Sau đó, hàm băm chuyển sang "giá trị khóa của tôi là gì? Ồ, đó là "ruby". Sau đó, ánh xạ giá trị đó thành" màu đỏ "hoặc" lập trình ". Nói cách khác, :rubykhông tham chiếu đến "red"hoặc "programming". Hàm băm ánh xạ :ruby tới "red"hoặc "programming".

So sánh điều đó với nếu chúng ta sử dụng các ký hiệu

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

Giá trị của :rubycũng "ruby", một cách hiệu quả.

Tại sao? Vì bản chất các ký hiệu là hằng chuỗi . Hằng số không có nhiều trường hợp. Đó là cùng một địa chỉ bộ nhớ. Và một địa chỉ bộ nhớ có một giá trị nhất định, khi đã được tham chiếu. Đối với các biểu tượng, tên con trỏ là biểu tượng và giá trị được tham chiếu là một chuỗi, khớp với tên biểu tượng, trong trường hợp này "ruby",.

Khi ở trong hàm băm, bạn không sử dụng ký hiệu, con trỏ, mà là giá trị được tham chiếu. Bạn không sử dụng :ruby, nhưng "ruby". Sau đó, hàm băm sẽ tìm kiếm khóa "ruby", giá trị là "red"hoặc "programming", tùy thuộc vào cách bạn xác định hàm băm.

Khái niệm chuyển đổi mô hình và lấy về nhà là giá trị của một biểu tượng là một khái niệm hoàn toàn tách biệt với một giá trị được ánh xạ tới bởi một hàm băm, được cung cấp cho một khóa của hàm băm đó.


những gì sai lầm hoặc sai lầm trong giải thích này, những người phản đối? tò mò vì lợi ích của việc học.
ahnbizcad

chỉ vì một phép loại suy có thể khó chịu đối với một số người, không có nghĩa là nó sai.
ahnbizcad

2
Tôi không thấy bất kỳ lỗi nào, nhất thiết, nhưng rất khó để xác định những gì bạn đang cố gắng nói trong câu trả lời này.
Thiết kế ngược vào

x được quan tâm và khái niệm hóa khác nhau dựa trên ngữ cảnh / thực thể thực hiện phiên dịch. khá đơn giản.
ahnbizcad

đại tu câu trả lời. Cảm ơn vì bạn đã phản hồi.
ahnbizcad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.