development

Ruby에서 기호를 이해하는 방법

big-blog 2020. 10. 4. 11:57
반응형

Ruby에서 기호를 이해하는 방법


" Ruby Symbols 이해하기 "를 읽었음에도 불구하고 , 저는 기호 를 사용할 때 메모리에있는 데이터의 표현에 여전히 혼란 스럽습니다. 다른 객체에 포함 된 두 개의 심볼이 동일한 메모리 위치에 존재하는 경우 어떻게 다른을 포함하고 있습니까? 동일한 메모리 위치에 동일한 값이 포함될 것으로 예상했습니다.

이것은 링크의 인용문입니다.

문자열과 달리 동일한 이름의 심볼은 초기화되어 루비 세션 중 한 번만 메모리에 존재합니다.

동일한 메모리 위치에 포함 된 값을 구별하는 방법을 이해하지 못합니다.

이 예를 고려하십시오.

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

patient1그리고 patient2두 해시, 괜찮아요 있습니다. :ruby그러나 상징입니다. 다음을 출력하려면 :

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

그러면 무엇이 출력됩니까? "red", 또는 "programming"?

잠시 동안 해시를 잊어 버리면 기호가 값에 대한 포인터 라고 생각 합니다. 내가 가진 질문은 다음과 같습니다.

  • 기호에 값을 지정할 수 있습니까?
  • 기호는 값이있는 변수에 대한 포인터 일 뿐입니 까?
  • 심볼이 전역이라면 심볼이 항상 한 가지를 가리킴을 의미합니까?

이걸 고려하세요:

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

따라서 심볼 객체를 만들면 내용이 동일하면 메모리에서 동일한 객체를 참조합니다. 심볼은 불변 객체 이기 때문에 문제가되지 않습니다 . 문자열은 변경 가능합니다.


(아래 댓글에 대한 답변)

원본 기사에서 값은 기호에 저장되지 않고 해시에 저장됩니다. 이걸 고려하세요:

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

이것은 메모리에 4 개의 문자열 객체와 2 개의 해시 객체의 6 개의 객체를 생성합니다.

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

이것은 메모리에 5 개의 객체 (심볼 1 개, 문자열 2 개, 해시 객체 2 개) 만 생성합니다.


이런 식으로 생각하면 상징을 그을 수있었습니다. Ruby 문자열은 메서드와 속성이 많은 객체입니다. 사람들은 키에 문자열을 사용하는 것을 좋아하고 문자열이 키에 사용되면 이러한 모든 추가 메서드가 사용되지 않습니다. 그래서 그들은 좋은 키가되기 위해 필요한 것을 제외하고 모든 기능이 제거 된 문자열 객체 인 심볼을 만들었습니다.

기호를 상수 문자열로 생각하면됩니다.


기호는 :ruby포함하지 않는 "red""programming". 기호 :ruby는 단지 기호 :ruby입니다. 그것은 당신의 해시이며 patient1, patient2각각은 같은 키가 가리키는 각각의 값을 포함합니다.

이렇게 생각해보십시오. 크리스마스 아침에 거실에 들어가서 "Kezzer"라고 적힌 태그가 달린 상자 두 개를 보면 On에는 양말이 있고 다른 하나에는 석탄이 있습니다. 이름이 같더라도 "Kezzer"가 어떻게 양말과 석탄을 모두 포함 할 수 있는지 혼동하지 않을 것입니다. 이름에 (엉뚱한) 선물이 포함되어 있지 않기 때문입니다. 그것은 단지 그들을 가리키는 것입니다. 마찬가지로 :ruby해시의 값을 포함하지 않고 해당 값을 가리 킵니다.


당신은 당신이 만든 선언이 Symbol의 가치를 그것이 무엇인지 아닌 다른 것으로 정의한다고 가정 할 수 있습니다. 실제로 Symbol은 일정하게 유지되는 "내부화 된"문자열 값입니다. 많은 수의 가변 길이 문자열을 관리하는 것보다 효율적으로 자주 사용되는 단순한 정수 식별자를 사용하여 저장되기 때문입니다.

귀하의 예를 들어보십시오.

patient1 = { :ruby => "red" }

이것은 "patient1 변수를 선언하고 그것을 해시로 정의하고,이 저장소에서 키 아래에 'red'값 (기호 'ruby')"으로 읽어야합니다.

이것을 작성하는 또 다른 방법은 다음과 같습니다.

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

puts patient1[:ruby]
# 'red'

할당을 할 때 반환되는 결과가 처음에 할당 한 결과와 동일하다는 것은 놀라운 일이 아닙니다.

Symbol 개념은 대부분의 다른 언어의 기능이 아니므로 약간 혼란 스러울 수 있습니다.

값이 동일하더라도 각 String 객체는 구별됩니다.

[ "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

동일한 값을 가진 모든 기호는 동일한 객체를 참조합니다.

[ :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

문자열을 기호로 변환하면 동일한 값이 동일한 고유 기호에 매핑됩니다.

[ "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

마찬가지로 Symbol에서 String으로 변환하면 매번 고유 한 문자열이 생성됩니다.

[ :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

Symbol 값은 내부 Hash 테이블에서 가져온 것으로 생각할 수 있으며 간단한 메서드 호출을 사용하여 Symbols로 인코딩 된 모든 값을 볼 수 있습니다.

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=, ...

콜론 표기법이나 .to_sym을 사용하여 새 기호를 정의하면이 테이블이 커집니다.


Symbols are not pointers. They do not contain values. Symbols simply are. :ruby is the symbol :ruby and that's all there is to it. It doesn't contain a value, it doesn't do anything, it just exists as the symbol :ruby. The symbol :ruby is a value just like the number 1 is. It doesn't point to another value any more than the number 1 does.


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

Then what will be output? "red", or "programming"?

Neither, it will output "ruby".

You're confusing symbols and hashes. They aren't related, but they're useful together. The symbol in question is :ruby; it has nothing to do with the values in the hash, and it's internal integer representation will always be the same, and it's "value" (when converted to a string) will always be "ruby".


In short

Symbols solve the problem of creating human readable, immutable representations that also have the benefit of being simpler for the runtime to lookup than strings. Think of it like a name or label that can be reused.

Why :red is better than "red"

In dynamic object oriented languages you create complex, nested data structures with readable references. The hash is a common use case where you map values to unique keys — unique, at least, to each instance. You can't have more than one "red" key per hash.

However it would be more processor efficient to use a numeric index instead of string keys. So symbols were introduced as a compromise between speed and readability. Symbols resolve much easier than the equivalent string. By being human readable and easy for the runtime to resolve symbols are an ideal addition to a dynamic language.

Benefits

Since symbols are immutable they can be shared across the runtime. If two hash instances have a common lexicographic or semantic need for a red item the symbol :red would use roughly half the memory that the string "red" would have required for two hashes.

Since :red always resolves back to the same location in memory it can be reused across a hundred hash instances with almost no increase in memory, whereas using "red" will add a memory cost since each hash instance would need to store the mutable string upon creation.

Not sure how Ruby actually implements symbols/string but clearly a symbol offers less implementation overhead in the runtime since it's a fixed representation. Plus symbols takes one less character to type than a quoted string and less typing is the eternal pursuit of true Rubyists.

Summary

With a symbol like :red you get the readability of string representation with less overhead due to the cost of string comparison operations and the need to store each string instance in memory.


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

patient1 and patient2 are both hashes, that's fine. :ruby however is a symbol. If we were to output the following:

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

Then what will be output? "red", or "programming"?

Neither, of course. The output will be ruby. Which, BTW, you could have found out in less time than it took you to type the question, by simply typing it into IRB instead.

Why would it be red or programming? Symbols always evaluate to themselves. The value of the symbol :ruby is the symbol :ruby itself and the string representation of the symbol :ruby is the string value "ruby".

[BTW: puts always converts its arguments to strings, anyway. There's no need to call to_s on it.]


I would recommend reading the Wikipedia article on hash tables - I think it will help you get a sense of what {:ruby => "red"} really means.

Another exercise that might help your understanding of the situation: consider {1 => "red"}. Semantically, this doesn't mean "set the value of 1 to "red"", which is impossible in Ruby. Rather, it means "create a Hash object, and store the value "red" for the key 1.


I'm new to Ruby, but I think (hope?) this is a simple way to look at it...

A symbol is not a variable or a constant. It doesn't stand in for, or point to, a value. A symbol IS a value.

All it is, is a string without the object overhead. The text and only the text.

So, this:

"hellobuddy"

Is the same as this:

:hellobuddy

Except you can't do, for example, :hellobuddy.upcase. It's the string value and ONLY the string value.

Likewise, this:

greeting =>"hellobuddy"

Is the same as this:

greeting => :hellobuddy

But, again, without the string object overhead.


One easy way to wrap your head around this is to think, "what if I were using a string rather than a symbol?

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

It isn't confusing at all, right? You're using "ruby" as a key in a hash.

"ruby" is a string literal, so that is the value. The memory address, or pointer, is not available to you. Every time you invoke "ruby", you are creating a new instance of it, that is, creating a new memory cell containing the same value - "ruby".

The hash then goes "what's my key value? Oh it's "ruby". Then maps that value to "red" or "programming". In other words, :ruby doesn't dereference to "red" or "programming". The hash maps :ruby to "red" or "programming".

Compare that to if we use symbols

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

The value of :ruby is also "ruby", effectively.

Why? Because symbols are essentially string constants. Constants don't have multiple instances. It's the same memory address. And a memory address has a certain value, once dereferenced. For symbols, the pointer name is the symbol, and the dereferenced value is a string, which matches the symbol name, in this case, "ruby".

When in a hash, you are not using the symbol, the pointer, but the deferenced value. You're not using :ruby, but "ruby". The hash then looks up for key "ruby", the value is "red" or "programming", depending on how you defined the hash.

The paradigm shift and take-home concept is that a symbol's value is a completely separate concept from a value mapped to by a hash, given a key of that hash.

참고URL : https://stackoverflow.com/questions/2341837/how-to-understand-symbols-in-ruby

반응형