Ruby에서 문자열 대신 기호를 사용하는 경우


98

스크립트에 동일한 문자열의 인스턴스가 두 개 이상있는 경우 대신 기호를 사용해야합니까?

답변:


175

TL; DR

간단한 경험 법칙은 내부 식별자가 필요할 때마다 기호를 사용하는 것입니다. Ruby <2.2의 경우 메모리 누수를 방지하기 위해 동적으로 생성되지 않는 경우에만 기호를 사용합니다.

전체 답변

동적으로 생성되는 식별자에 사용하지 않는 유일한 이유는 메모리 문제 때문입니다.

이 질문은 많은 프로그래밍 언어에 기호가없고 문자열 만 있으므로 코드에서 식별자로도 사용되기 때문에 매우 일반적입니다. 당신은 심볼이 무엇을 걱정해야 될 운명 뿐만 아니라 당신이 기호를 사용해야 할 때 . 기호는 식별자를 의미합니다. 이 철학을 따르면 일을 올바르게 할 가능성이 있습니다.

기호와 문자열의 구현에는 몇 가지 차이점이 있습니다. 심볼의 가장 중요한 점은 불변 이라는 것입니다. 입니다. 이것은 그들이 그들의 가치를 결코 변하지 않을 것이라는 것을 의미합니다. 이 때문에 심볼은 문자열보다 빠르게 인스턴스화되며 두 심볼을 비교하는 것과 같은 일부 작업도 더 빠릅니다.

심볼이 변경 불가능하다는 사실로 인해 Ruby는 심볼을 참조 할 때마다 동일한 객체를 사용하여 메모리를 절약 할 수 있습니다. 따라서 인터프리터가 읽을 때마다 :my_key다시 인스턴스화하는 대신 메모리에서 가져올 수 있습니다. 매번 새 문자열을 초기화하는 것보다 비용이 적게 듭니다.

다음 명령으로 이미 인스턴스화 된 모든 기호 목록을 가져올 수 있습니다 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.

2.2 이전의 Ruby 버전의 경우 심볼이 인스턴스화되면이 메모리는 다시 는 사용 가능 하지 않습니다 . 메모리를 해제하는 유일한 방법은 응용 프로그램을 다시 시작하는 것입니다. 따라서 기호는 잘못 사용되었을 때 메모리 누수의 주요 원인이기도합니다. 메모리 누수를 생성하는 가장 간단한 방법 to_sym은 사용자 입력 데이터에 대한 방법 을 사용하는 것입니다.이 데이터는 항상 변경되므로 메모리의 새로운 부분이 소프트웨어 인스턴스에서 영원히 사용됩니다. Ruby 2.2는 기호 가비지 수집기를 도입했습니다. 동적으로 생성 된 심볼을 해제 . 따라서 심볼을 동적으로 생성하여 생성 된 메모리 누수는 더 이상 문제가되지 않습니다.

질문에 대한 답변 :

내 응용 프로그램이나 스크립트에 동일한 문자열이 두 개 이상 있으면 문자열 대신 기호를 사용해야한다는 것이 사실입니까?

찾고있는 것이 코드 내부에서 사용되는 식별자 인 경우 기호를 사용해야합니다. 출력물을 인쇄하는 경우 문자열이 두 번 이상 나타나더라도 메모리에 두 개의 다른 개체를 할당하더라도 문자열을 사용해야합니다.

이유는 다음과 같습니다.

  1. 기호를 인쇄하는 것은 문자열로 캐스트되기 때문에 인쇄하는 것보다 느립니다.
  2. 다른 심볼이 많이 있으면 할당이 해제되지 않기 때문에 애플리케이션의 전체 메모리 사용량이 증가합니다. 그리고 코드의 모든 문자열을 동시에 사용하지 않습니다.

@AlanDert의 사용 사례

@AlanDert : 햄 코드에서 % input {type : : checkbox} 같은 것을 여러 번 사용하면 체크 박스로 무엇을 사용해야합니까?

나 : 네.

@AlanDert :하지만 html 페이지에 기호를 인쇄하려면 문자열로 변환해야합니다. 그렇지 않나요? 그렇다면 그것을 사용하는 요점은 무엇입니까?

입력 유형은 무엇입니까? 사용하려는 입력 유형의 식별자 또는 사용자에게 보여주고 싶은 것?

언젠가는 HTML 코드가되는 것은 사실이지만 코드 줄을 작성하는 순간에는 식별자가된다는 의미입니다. 필요한 입력 필드의 종류를 식별합니다. 따라서 코드에서 반복해서 사용되며 항상 식별자와 동일한 "문자열"문자를 가지며 메모리 누수를 생성하지 않습니다.

즉, 문자열이 더 빠른지 확인하기 위해 데이터를 평가하지 않는 이유는 무엇입니까?

이것은 내가 이것을 위해 만든 간단한 벤치 마크입니다.

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

세 가지 출력 :

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

따라서 smbol을 사용하는 것이 실제로 문자열을 사용하는 것보다 약간 빠릅니다. 왜 그런 겁니까? HAML이 구현되는 방식에 따라 다릅니다. 확인하려면 HAML 코드를 약간 해킹해야하지만 식별자 개념에서 기호를 계속 사용하면 응용 프로그램이 더 빠르고 안정적입니다. 질문이 제기되면 벤치마킹하고 답을 얻으십시오.


@andrewcockerham 제공된 링크가 작동하지 않습니다 (오류 -404). 링크에서 마지막 /(이후 strings) 을 제거해야합니다 . 여기있다 : www.reactive.io/tips/2009/01/11/the-difference-between-ruby- 상징 - 및 - 문자열
아툴 Khanduri

14

간단히 말해서 기호는 문자로 구성된 이름이지만 변경할 수 없습니다. 반대로 문자열은 내용이 변경 될 수있는 문자에 대한 정렬 된 컨테이너입니다.


4
+1. 기호와 문자열은 완전히 다른 것입니다. 잘못 배운 경우가 아니라면 어떤 것을 사용해야하는지에 대한 혼동이 없습니다 (즉, "기호는 불변의 문자열입니다"오류).
Jörg W Mittag

@ JörgWMittag : 맞습니다.
Boris Stitnicky

5
당신은 요점이 있지만 질문에 대답하지 마십시오. OP는 문자열과 기호를 혼동하고 있습니다. 다른 것을 말하는 것으로는 충분하지 않습니다-그들이 닮은 점과 다른 점을 이해하도록 도와야합니다
fotanus

1
웹에서 일어나는 @ JörgWMittag는 문서를 살펴 보거나 실제로있는 그대로 설명하는 사람들을 찾을만큼 운이 좋은 경우가 아니라면 보입니다.
sargas

8
  1. 루비 기호는 O (1) 비교가있는 객체입니다.

두 문자열을 비교하려면 잠재적으로 모든 문자를 살펴볼 필요가 있습니다. 길이가 N 인 두 문자열의 경우 N + 1 비교가 필요합니다 (컴퓨터 과학자가 "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"

그러나 : foo의 모든 모양은 동일한 객체를 참조하므로 객체 ID를보고 기호를 비교할 수 있습니다. 단일 비교 (컴퓨터 과학자들이 "O (1) 시간"이라고 함)로이를 수행 할 수 있습니다.

def symbol_comp sym1, sym2
  sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
  1. Ruby 기호는 자유 형식 열거의 레이블입니다.

C ++에서 "열거"를 사용하여 관련 상수 패밀리를 나타낼 수 있습니다.

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

그러나 Ruby는 동적 언어이기 때문에 BugStatus 유형을 선언하거나 합법적 인 값을 추적하는 것에 대해 걱정할 필요가 없습니다. 대신 열거 형 값을 기호로 나타냅니다.

original_status = :open
current_status  = :closed

3. Ruby 심볼은 일정하고 고유 한 이름입니다.

Ruby에서는 문자열의 내용을 변경할 수 있습니다.

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

그러나 심볼의 내용은 변경할 수 없습니다.

:foo[0]  = ?b # Raises an error
  1. Ruby 기호는 키워드 인수의 키워드입니다.

Ruby 함수에 키워드 인수를 전달할 때 기호를 사용하여 키워드를 지정합니다.

# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
        :action => 'show',
        :id => bug.id
  1. Ruby 기호는 해시 키에 대한 탁월한 선택입니다.

일반적으로 해시 테이블의 키를 나타내는 기호를 사용합니다.

options = {}
options[:auto_save]     = true
options[:show_comments] = false

5

다음은 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."

출력은 다음과 같습니다.

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.

2
이것이 1/10 초라는 사실을 잊지 말자.
Casey

그것은 모두 상대적입니다. 때로는 백분의 일이 중요합니다.
Yurii

2
백만 번의 반복을 통해 100 분의 1 초? 그것이 당신에게 가능한 최고의 최적화라면 당신의 프로그램은 이미 꽤 잘 최적화되었다고 생각합니다.
Casey

0
  • 기호를 해시 키 식별자로 사용

    {key: "value"}

  • 기호를 사용하면 다른 순서로 메서드를 호출 할 수 있습니다.

     def write (파일 :, 데이터 :, 모드 : "ascii")
          # 간결함을 위해 제거됨
     종료
     쓰기 (데이터 : 123, 파일 : "test.txt")
  • 고정하여 문자열로 유지하고 메모리를 절약하십시오.

    label = 'My Label'.freeze

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