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


85

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

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

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

1
Symbol을 인쇄하기 때문에 ": ruby"가 출력됩니다. 라고하면 puts patient1[:ruby]"빨간색"이 인쇄되고이라고 말하면 puts patient2[:ruby]"프로그래밍"이 인쇄됩니다.
ROSS

1
기호는 값에 대한 포인터가 아닙니다. 내부적으로 기호는 정수일뿐입니다.
akuhn

답변:


62

이걸 고려하세요:

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 개) 만 생성합니다.


그러나 링크의 예는 다른 값을 포함하는 기호를 보여 주지만 기호의 이름과 메모리 위치는 같습니다. 그들이 출력 될 때, 그들은 다른 값 을 가지고 있습니다. 그것은 제가 얻지 못하는 부분입니다. 확실히 동일한 값을 포함해야합니까?
Kezzer

1
나는 내가 아직도 어떻게 혼란스러워하는지 설명하기 위해 편집을했다. 내 두뇌는 계산할 수 없습니다;)
Kezzer

48
기호 값을 포함하지 않는, 그들은 있다 값. 해시는 값을 포함합니다.
Mladen Jablanović

5
그것은있어 Hash저장하는 키 / 값 쌍은 아니라는 것을 ({... => ...} 코드에서 만든) Symbol자신이야. Symbol들 (예를 들어, :symbol또는 :sym또는 :ruby) 쌍의 키입니다. 의 일부로 만 Hash그들은 무엇이든 "지키"합니다.
James A. Rosen

1
기호는 값이 아닌 해시에서 키로 사용되고 있으므로 서로 다를 수 있습니다. 이는 key1 = 'ruby'및 hash1 = {key1 => 'value'...} hash2 = { key1 => '값 2'...}.
여호수아 올슨

53

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

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


2
게시물을 읽어 보면이 글이 가장 의미가있을 것입니다. : ruby는 메모리 어딘가에 저장됩니다. "ruby"를 어딘가에 사용하고 다시 "ruby"를 다시 어딘가에 사용하면 복제 일뿐입니다. 따라서 기호를 사용하는 것은 공통 데이터의 중복을 줄이는 방법입니다. 당신이 말했듯이, 상수 문자열. 그 기호를 다시 사용할 기본 메커니즘이 있다고 생각합니까?
Kezzer

@Kezzer이 대답은 정말 좋고 나에게 옳은 것처럼 보이지만 귀하의 의견은 다른 것을 말하고 잘못되었거나 오해의 소지가 있으며 귀하의 의견은 문자열을 사용한 데이터 중복에 대해 말하며 기호의 이유입니다. 그것은 잘못되었거나 오해의 소지가 있습니다. 기호가 여러 번 더 많은 메모리 공간을 사용하지는 않지만 많은 언어에서 문자열에 대해서도이를 가질 수 있습니다. 예를 들어 "abc"를 작성하고 다른 곳에 "abc"를 작성하면 컴파일러는 동일한 값 문자열을보고 저장합니다. 같은 장소에서 동일한 객체를 만드는데이를 string interning이라고하며 C #은 그렇게합니다.
barlop

그래서 그것은 기본적으로 믿을 수 없을 정도로 가벼운 문자열 버전입니까?
stevec

34

기호는 :ruby포함하지 않는 "red""programming". 상징 :ruby은 단지 상징 :ruby입니다. 그것은 당신의 해시입니다, patient1그리고 patient2각각 그 값을 포함하는 것으로, 각각의 경우에 동일한 키에 의해 지적했다.

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


2
이 대답은 완전히 의미가 있습니다.
Vass

이것은 해시와 기호가 완전히 혼합 된 것처럼 들립니다. 기호는 값을 가리 키지 않습니다. 만약 당신이 해시에있을 때 그렇다고 말하고 싶다면, 그것은 논쟁의 여지가있을 수 있지만, 기호가 해시에있을 필요는 없습니다. mystring = :steveT 기호가 아무 것도 가리 키지 않는다고 말할 수 있습니다 . 해시의 키에는 관련 값이 있으며 키는 기호가 될 수 있습니다. 그러나 기호는 해시에있을 필요가 없습니다.
barlop

27

당신은 당신이 만든 선언이 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을 사용하여 새 기호를 정의하면이 테이블이 커집니다.


17

기호는 포인터가 아닙니다. 값을 포함하지 않습니다. 기호는 간단 하다 . :ruby상징 :ruby이고 그게 전부입니다. 그것은하지 않습니다 값을 포함하지 않는 수행 은 단지 상징으로 존재, 아무것도 :ruby. 기호 :ruby는 숫자 1과 같은 값입니다. 숫자 1이하는 것보다 더 다른 값을 가리 키지 않습니다.


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

그러면 무엇이 출력됩니까? "빨간색"또는 "프로그래밍"?

둘 다 "루비"를 출력합니다.

기호와 해시를 혼동하고 있습니다. 관련이 없지만 함께 유용합니다. 문제의 기호는 다음과 같습니다 :ruby. 해시의 값과 관련이 없으며 내부 정수 표현은 항상 동일하며 "값"(문자열로 변환 될 때)은 항상 "루비"가됩니다.


10

간단히 말해서

심볼은 사람이 읽을 수 있고 변경할 수없는 표현을 만드는 문제를 해결하며 런타임이 문자열보다 조회하는 것이 더 간단하다는 이점도 있습니다. 재사용 할 수있는 이름이나 레이블로 생각하십시오.

왜 : red가 "red"보다 낫다

동적 객체 지향 언어에서는 읽을 수있는 참조로 복잡하고 중첩 된 데이터 구조를 만듭니다. 해시는 일반적인 사용 사례입니다 이상, 각 인스턴스에 고유 한 - 당신이 고유 키 값을 매핑합니다. 해시 당 하나 이상의 "빨간색"키를 가질 수 없습니다.

그러나 문자열 키 대신 숫자 인덱스를 사용하는 것이 더 프로세서 효율적입니다. 따라서 기호는 속도와 가독성 사이의 절충안으로 도입되었습니다. 기호는 동등한 문자열보다 훨씬 쉽게 해석됩니다. 사람이 읽을 수 있고 런타임에서 기호를 쉽게 확인할 수 있으므로 동적 언어에 이상적인 추가 기능입니다.

혜택

심볼은 변경 불가능하므로 런타임에서 공유 할 수 있습니다. 두 개의 해시 인스턴스가 빨간색 항목에 대한 공통 사전 적 또는 의미 론적 필요를 갖는 경우 : red 기호는 두 개의 해시에 대해 문자열 "red"가 필요로하는 메모리의 약 절반을 사용합니다.

: red는 항상 메모리의 동일한 위치로 다시 확인되므로 메모리를 거의 늘리지 않고 백 개의 해시 인스턴스에서 재사용 할 수 있지만 "red"를 사용하면 각 해시 인스턴스가 변경 가능한 문자열을 저장해야하기 때문에 메모리 비용이 추가됩니다. 창조.

Ruby가 실제로 심볼 / 문자열을 구현하는 방법은 확실하지 않지만 심볼은 고정 된 표현이기 때문에 런타임에서 구현 오버 헤드가 적습니다. 더하기 기호는 따옴표로 묶인 문자열보다 입력하는 데 문자가 하나 더 적으며, 더 적은 입력은 진정한 Rubyists의 영원한 추구입니다.

요약

: red와 같은 기호를 사용하면 문자열 비교 작업의 비용과 각 문자열 인스턴스를 메모리에 저장해야하기 때문에 오버 헤드가 적은 문자열 표현의 가독성을 얻을 수 있습니다.


4

해시 테이블에 대한 Wikipedia 기사를 읽는 것이 좋습니다 {:ruby => "red"}. 이것이 실제로 의미하는 바를 이해하는 데 도움이 될 것이라고 생각합니다 .

상황을 이해하는 데 도움이되는 또 다른 연습은 다음과 같습니다 {1 => "red"}. 의미, 이것은 의미하지 않는다 "값 설정 1"red"루비 불가능하다". 오히려 " Hash 개체를 만들고 "red"키 값 을 저장합니다 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

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

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

그러면 무엇이 출력됩니까? "빨간색"또는 "프로그래밍"?

물론 둘 다 아닙니다. 출력은ruby . BTW, 질문을 입력하는 데 걸리는 시간보다 IRB에 간단히 입력하는 것보다 짧은 시간에 알아낼 수있었습니다.

이 수 red또는 programming? 기호는 항상 자체적으로 평가됩니다. 기호의 값은 :ruby기호 :ruby자체이고 기호 의 문자열 표현은 :ruby문자열 값 "ruby"입니다.

[BTW : puts어쨌든 항상 인수를 문자열로 변환합니다. 전화 할 필요가 없습니다 to_s.]


현재 컴퓨터에 IRB가없고 설치도 할 수 없기 때문에 죄송합니다.
Kezzer

2
@Kezzer : 걱정하지 마세요. 그냥 궁금했습니다. 때때로 당신은 더 이상 가장 단순한 것을 볼 수 없을 정도로 문제에 너무 깊이 묻혀 있습니다. 기본적으로 IRB에 귀하의 질문을 잘라내어 붙여 넣었을 때 "그가 직접 그렇게하지 않은 이유는 무엇입니까?" 그리고 걱정하지 마세요. 대답이 "그냥 실행하세요!"라고 대답 할 때 "이 인쇄는 무엇입니까?" BTW : 언제 어디서나 설치가 필요없는 인스턴트 IRB입니다. TryRuby.Org 또는 Ruby-Versions.Net 은 지금까지 출시 된 MRI + YARV + JRuby + Rubinius + REE의 모든 버전에 대한 SSH 액세스를 제공합니다.
Jörg W Mittag

고마워요, 그냥 가지고 놀아요. 나는 그것을 다시 살펴 보지만 여전히 약간 혼란 스럽습니다.
Kezzer

0

저는 Ruby를 처음 접했지만 (희망?) 이것이 그것을 보는 간단한 방법이라고 생각합니다 ...

기호는 변수 나 상수가 아닙니다. 그것은 가치를 의미하거나 가리 키지 않습니다. 기호는 값입니다.

그저 객체 오버 헤드가없는 문자열입니다. 텍스트와 텍스트 만.

그래서 이건:

"hellobuddy"

다음과 같습니다.

:hellobuddy

예를 들어 : hellobuddy.upcase와 같이 할 수 없습니다. 그것은 문자열 값이고 문자열 값입니다.

마찬가지로 다음과 같습니다.

greeting =>"hellobuddy"

다음과 같습니다.

greeting => :hellobuddy

그러나 다시 문자열 객체 오버 헤드없이.


-1

머리를 감는 한 가지 쉬운 방법은 "심볼이 아닌 문자열을 사용한다면 어떨까요?"라고 생각하는 것입니다.

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

전혀 혼란스럽지 않죠? 해시 의 키로 "ruby"를 사용하고 있습니다.

"ruby"문자열 리터럴이므로 값입니다. 메모리 주소 또는 포인터를 사용할 수 없습니다. 을 호출 할 때마다 "ruby"새 인스턴스를 생성합니다 "ruby". 즉, 동일한 값을 포함하는 새 메모리 셀을 생성합니다 .

해시는 "내 키 값이야? 오 그건 간다 "ruby". 그런 다음에 그 값 매핑"즉, 빨간색 "또는"프로그래밍 ". :ruby에 역 참조하지 않습니다 "red"또는 "programming". 해시는 매핑 :ruby"red""programming".

기호를 사용하는 경우와 비교하십시오.

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

의 가치 :ruby"ruby", 효과적으로입니다.

왜? 기호는 본질적으로 문자열 상수 이기 때문 입니다. 상수에는 여러 인스턴스가 없습니다. 동일한 메모리 주소입니다. 그리고 메모리 주소는 일단 역 참조되면 특정 값을 갖습니다. 기호의 경우 포인터 이름은 기호이고 역 참조 된 값은 기호 이름 (이 경우 "ruby".

해시에서는 기호, 포인터가 아니라 종속 된 값을 사용합니다. 을 사용하지 :ruby않지만 "ruby". 그런 다음 해시는 해시를 정의한 방법에 따라 key를 찾고 "ruby"값은 "red"또는 "programming"입니다.

패러다임 전환과 집으로 가져가는 개념은 해당 해시의 키가 주어진 경우 기호의 값이 해시로 매핑 된 값과 완전히 별 개인 개념이라는 것입니다.


이 설명의 오류 또는 오류는 무엇입니까? 학습을 위해 호기심이 많습니다.
ahnbizcad

비유가 일부 사람들에게 불쾌감을 줄 수 있다고해서 결함이 있다는 의미는 아닙니다.
ahnbizcad

2
필연적으로 오류가 표시되지는 않지만이 답변에서 말하려는 내용을 정확히 파악하는 것은 매우 어렵습니다.
리버스 엔지니어링

x는 해석을 수행하는 컨텍스트 / 엔터티에 따라 다르게 인터 레팅되고 개념화됩니다. 아주 간단합니다.
ahnbizcad

대답을 점검했습니다. 피드백을 주셔서 감사합니다.
ahnbizcad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.