Ruby에서 기호를 해시 키로 사용하는 이유는 무엇입니까?


162

많은 사람들이 루비 해시에서 기호를 키로 사용합니다.

문자열을 사용하는 것의 장점은 무엇입니까?

예 :

hash[:name]

vs.

hash['name']

답변:


227

TL; DR :

기호를 사용하면 비교할 때 시간이 절약 될뿐만 아니라 한 번만 저장되므로 메모리도 절약됩니다.

루비 심볼은 변경할 수 없으며 변경할 수 없습니다.

짧은 답변 :

기호를 사용하면 비교할 때 시간이 절약 될뿐만 아니라 한 번만 저장되므로 메모리도 절약됩니다.

Ruby의 기호는 기본적으로 "불변 문자열"입니다 . 이는 변경할 수 없으며 소스 코드 전체에서 여러 번 참조 될 때 동일한 기호가 항상 동일한 엔티티로 저장됨을 의미합니다 (예 : 동일한 객체 ID를 가짐) .

반면에 문자열은 변경 가능 하며 언제든지 변경할 수 있습니다. 이는 Ruby가 소스 코드 전체에서 언급 한 각 문자열을 별도의 엔티티에 저장해야 함을 의미합니다. 예를 들어 소스 코드에 문자열 "name"이 여러 번 언급 된 경우 Ruby는 이들을 모두 별도의 String 객체에 저장해야합니다. 나중에 변경 될 수 있습니다 (루비 문자열의 특성).

문자열을 해시 키로 사용하는 경우, 루비는 문자열을 평가하고 그 내용을보고 (해시 함수 계산) 해시에 이미 저장된 키의 (해시 된) 값과 결과를 비교해야합니다. .

기호를 해시 키로 사용하는 경우, 그것은 불변이라는 것이 암시 적이므로 Ruby는 기본적으로 이미 저장된 키의 (해시 된) object-id와 (objected)의 (hash 함수)를 비교할 수 있습니다. 해시. (훨씬 더 빨리)

단점 : 각 심볼은 루비 인터프리터의 심볼 테이블에서 슬롯을 사용하는데,이 슬롯은 절대로 해제되지 않습니다. 심볼은 가비지 수집되지 않습니다. 따라서 코너 케이스는 많은 수의 심볼 (예 : 자동 생성 된 심볼)이있는 경우입니다. 이 경우 이것이 Ruby 인터프리터의 크기에 어떤 영향을 미치는지 평가해야합니다.

노트:

문자열 비교를 수행하면 Ruby는 기호를 평가하지 않고 객체 ID로만 기호를 비교할 수 있습니다. 문자열을 비교하는 것보다 훨씬 빠릅니다. 평가해야합니다.

해시에 액세스하면 Ruby는 항상 해시 함수를 적용하여 사용하는 키에서 "해시 키"를 계산합니다. MD5 해시와 같은 것을 상상할 수 있습니다. 그리고 루비는이 "해시 키"를 서로 비교합니다.

긴 대답 :

https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/


5
참고로, 다음 루비 버전에서는 Symbols가 GCd가 될 것입니다 : bugs.ruby-lang.org/issues/9634
Ajedi32

2
또한 문자열은 Ruby에서 해시 키로 사용될 때 자동으로 고정됩니다. 따라서이 맥락에서 문자열에 대해 이야기 할 때 문자열을 변경할 수 있다는 것은 사실이 아닙니다.
Ajedi32

1
"긴 답변"섹션의 주제 및 첫 번째 링크에 대한 통찰력이 제거되거나 마이그레이션되었습니다.
Hbksagar

2
심볼은 루비 2.2에서 가비지 수집
Marc-André Lafortune

2
좋은 답변입니다! 트롤링 측면에서 "짧은 대답"도 충분합니다. ;)
technophyle

22

그 이유는 문자열보다 여러 가지 이득을 갖는 효율성입니다.

  1. 기호는 변경할 수 없으므로 "키가 변경되면 어떻게됩니까?" 묻지 않아도됩니다.
  2. 문자열은 코드에서 복제되며 일반적으로 메모리에서 더 많은 공간을 차지합니다.
  3. 해시 조회는 키의 해시를 계산하여 키를 비교해야합니다. 이다 O(n)기호에 대한 문자열과 일정에 대해.

또한 Ruby 1.9는 기호 키가있는 해시에 대한 간단한 구문 (예 :)을 도입 h.merge(foo: 42, bar: 6)했으며 Ruby 2.0에는 기호 키에 대해서만 작동하는 키워드 인수 가 있습니다.

참고 사항 :

1) Ruby가 String키를 다른 유형과 다르게 취급한다는 사실에 놀랄 수도 있습니다 . 과연:

s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash   # must be called whenever a key changes!
h[s]   # => nil, not "bar"
h.keys
h.keys.first.upcase!  # => TypeError: can't modify frozen string

문자열 키의 경우, Ruby는 객체 자체 대신 고정 된 사본을 사용합니다.

2) 문자 "b", "a"및 "r"은 :bar프로그램에서 발생할 때마다 한 번만 저장됩니다 . Ruby 2.2 이전에는 Symbols재사용하지 않은 새로운 것을 지속적으로 생성하는 것이 좋지 않았습니다. 글로벌 Symbol 조회 테이블에 영원히 남아 있기 때문입니다. 루비 2.2는 가비지를 수집하므로 걱정할 필요가 없습니다.

3) 실제로 객체 ID가 직접 사용 되었기 때문에 Ruby 1.8.x에서는 Symbol의 해시를 계산하는 데 시간이 걸리지 않았습니다.

:bar.object_id == :bar.hash # => true in Ruby 1.8.7

Ruby 1.9.x에서는 해시가 한 세션에서 다른 세션으로 변경됨에 따라 변경되었습니다 Symbols.

:bar.hash # => some number that will be different next time Ruby 1.9 is ran

탁월한 메모를 위해 +1하십시오! 나는 읽기가 더 쉬워지기 때문에 원래 해시 함수에 대해 언급하지 않았습니다. :)
Tilo

@Tilo : 나는 내 대답을 쓴 이유 :-) 난 그냥 루비 1.9에서 특수 구문을 언급하는 내 대답을 편집 루비 2.0의 약속 명명 된 매개 변수 참, 그건
마크 - 앙드레 Lafortune

해시 조회가 기호에 대해 일정하고 문자열에 대해 O (n)이 어떻게 일정한지 설명 할 수 있습니까?
Asad Moosvi

7

다시 : 문자열을 사용하는 것보다 장점은 무엇입니까?

  • 스타일링 : 루비 방식
  • (매우) 기호를 해시하는 것이 정수를 해시하는 것과 문자열을 해시하는 것과 동일하므로 약간 더 빠른 값 조회가 가능합니다.

  • 단점 : 프로그램의 심볼 테이블에서 해제되지 않은 슬롯을 사용합니다.


4
기호가 가비지 수집되지 않는다고 언급 한 경우 +1입니다.
Vortico

이 기호는 가비지 수집되지 않습니다
eudaimonia

0

Ruby 2.x에 도입 된 고정 문자열에 대한 후속 조치에 매우 관심이 있습니다.

텍스트 입력에서 오는 수많은 문자열을 처리 할 때 (예를 들어 랙을 통한 HTTP 매개 변수 또는 페이로드를 생각하고 있습니다) 어디서나 문자열을 사용하는 것이 더 쉽습니다.

당신이 수십 가지를 다룰 때도 결코 변하지 않을 때 (귀하의 사업이 "어휘"라면) 나는 그것들을 얼리면 변화를 가져올 수 있다고 생각합니다. 아직 벤치 마크를 수행하지는 않았지만 심볼 성능에 가깝습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.