Ruby 제작자가 Symbols 개념을 사용하기로 선택한 이유는 무엇입니까?


15

tl; dr : 기호에 대해 언어에 구애받지 않는 정의와 다른 언어로 표시해야하는 이유가 있습니까?

그렇다면 왜 루비 제작자 symbols가 언어 개념을 사용 했습니까?

나는 루비가 아닌 프로그래머의 관점에서 이것을 묻습니다. 나는 다른 많은 언어를 배웠고, 루비가 부르는 것을 다루고 있는지 아닌지를 명시해야 할 필요성을 발견했습니다 symbols.

주요 질문은 symbolsRuby 의 개념이 성능을 위해 존재합니까, 아니면 언어가 작성되는 방식 때문에 필요한 것입니까?

루비의 프로그램이 파이썬이나 자바 스크립트보다 더 가볍고 빠를까요? 그렇다면, 그 때문 symbols일까요?

루비의 의도 중 하나는 사람이 읽고 쓸 수 있도록하는 것이기 때문에, 다른 언어에서와 같이 인터프리터 자체에서 이러한 개선 사항을 구현함으로써 제작자가 코딩 프로세스를 간소화 할 수 없었습니까?

모두가 무엇 symbols이고 어떻게 사용 하는지 알고 싶어하는 이유 는 무엇인지 , 왜 처음부터 거기에 있는지 알고 싶지 않은 것 같습니다.


스칼라는 머리 꼭대기에 심볼이 있습니다. 나는 많은 Lisps가 생각합니다.
D. Ben Knoble

답변:


17

Ruby, Yukihiro "Matz"Matsumoto 제작자는 Ruby 가 Lisp, Smalltalk, Perl의 영향에 대해 설명했습니다 (Wikipedia는 Ada와 Eiffel도 말합니다).

Ruby는 다음 단계로 설계된 언어입니다.

  • 간단한 리스프 언어를 사용하십시오 (CL 이전의 언어).
  • 매크로 제거, s- 표현.
  • 간단한 객체 시스템을 추가하십시오 (CLOS보다 훨씬 간단합니다).
  • 고차 함수에서 영감을 얻은 블록을 추가하십시오.
  • 스몰 토크에있는 메소드를 추가하십시오.
  • Perl에서 찾을 수있는 기능을 추가합니다 (OO 방식).

그래서 루비는 이론적으로 Lisp였습니다.

이제부터 MatzLisp라고하겠습니다. ;-)

모든 컴파일러에서 함수, 변수, 명명 된 블록, 유형 등에 대한 식별자를 관리하게됩니다. 일반적으로 디버깅 정보를 추가 할 때를 제외하고는 컴파일러에 저장하고 생성 된 실행 파일에서 잊어 버립니다.

Lisp에서 이러한 심볼은 다른 패키지로 호스팅되는 일류 리소스이므로 런타임에 새로운 심볼을 추가하여 다른 종류의 객체에 바인딩 할 수 있습니다. 메타 프로그래밍시 코드의 다른 부분과 이름 충돌이 없는지 확인할 수 있으므로 유용합니다.

또한 심볼은 읽기 시간에 억지로 삽입되며 ID로 비교할 수 있습니다. 이는 숫자와 같은 추상적 인 과 같은 새로운 종류의 을 갖는 효율적인 방법 입니다. 이것은 정수로 뒷받침되는 자체 열거 형을 정의하는 대신 기호 값을 직접 사용하는 코드 작성에 도움이됩니다. 또한 각 기호는 추가 데이터를 보유 할 수 있습니다. 예를 들어, Emacs / Slime은 Emacs의 메타 데이터를 심볼의 속성 목록에 바로 첨부 할 수있는 방법입니다.

상징 의 개념은 Lisp의 중심입니다. 자세한 예는 PAIP (인공 지능 프로그래밍의 패러다임 : Norlip의 사례 연구)를 살펴보십시오 .


5
좋은 대답입니다. 그러나 나는 Matz에 동의하지 않습니다 : 나는 매크로없이 lisp 방언을 사용하지 않고 언어를 부르는 것을 생각하지 않을 것입니다. lisp의 런타임-메타 프로그래밍 기능은 정확하게이 언어에 놀라운 힘을 부여하는 것으로, 어마 어마한 단순하고 표현력있는 문법을 보완합니다.
cmaster-monica reinstate

11

그렇다면 왜 루비 제작자들은 symbols언어 개념을 사용해야 합니까?

글쎄, 그들은 엄격히 "해야"하지 않았으며, 선택했습니다. 또한 엄밀히 말하면 Symbols는 언어의 일부가 아니라 핵심 라이브러리의 일부입니다. 그들은 않는 언어 수준 리터럴 구문을 가지고 있지만, 당신이 호출하여 구성해야한다면 그들은 단지 잘 작동 Symbol::new.

나는 비 루비 프로그래머의 관점에서 그것을 이해하려고합니다. 나는 많은 다른 언어를 배웠고 루비가 부르는 것을 다루고 있는지 아닌지를 지정할 필요가 없다는 것을 알았습니다 symbols.

당신은 그 "다른 많은 언어들"이 무엇인지 말하지 않았지만, Symbol루비와 같은 데이터 타입 을 가진 언어의 작은 발췌문이 있습니다 :

Symbol다른 형태 의 기능을 제공하는 다른 언어도 있습니다 . 자바에서는, 예를 들어, 루비의 특징 String:의 두 (실제로는 3 개) 유형으로 분할 String하고 StringBuilder/을 StringBuffer. 반면에, 루비의 기능 Symbol유형은 자바로 접혀 String유형 : 자바 String들 수 구금 , 리터럴 문자열String평가 상수 식 자동 억류되어 컴파일 시간의 결과들, 동적으로 생성 String들 호출하여 구금 될 수있다 String.intern방법. StringJava 의 인턴 은 SymbolRuby 의 인턴 과 동일 하지만 별도의 유형으로 구현되지는 않으며 Java와 다른 상태입니다.String(참고 : 이전 버전의 Ruby에서는 이전에 String#to_sym호출 String#intern되었고 해당 메소드는 여전히 레거시 별명으로 존재합니다.)

주요 질문은 다음과 같습니다. symbolsRuby 의 개념이 자신과 다른 언어에 대한 성능 의도로 존재 합니까?

Symbols는 무엇보다도 특정 의미론을 가진 데이터 유형입니다 . 이러한 의미론은 일부 수행 가능한 작업 (예 : 빠른 O (1) 평등 테스트)을 구현할 수도 있지만 이것이 주된 목적은 아닙니다.

아니면 언어가 쓰여진 방식 때문에 존재해야하는 것입니까?

Symbol루비 언어에서는 전혀 필요하지 않습니다. 루비는 그것들 없이는 잘 작동합니다. 그것들은 순전히 라이브러리 기능입니다. 언어와 관련이있는 언어에는 정확히 한 곳이 있습니다 Symbol. def메소드 정의 표현식 Symbol은 정의중인 메소드의 이름을 나타내는 것으로 평가 됩니다. 그러나 이는 최근의 변경 사항으로, 그 전에는 반환 값을 지정하지 않은 채로 두었습니다. MRI는 단순히로 평가하고 nilRubinius는 Rubinius::CompiledMethod객체로 평가하는 등의 작업을 수행했습니다. UnboundMethod… 또는 단지 a 로 평가할 수도 있습니다 String.

루비의 프로그램이 파이썬이나 노드보다 더 가볍고 빠를까요? 그렇다면, 그 때문 symbols일까요?

나는 당신이 여기서 무엇을 요구하는지 잘 모르겠습니다. 성능은 대부분 언어가 아닌 구현 품질의 문제입니다. 또한 Node는 언어가 아니며 ECMAScript를위한 이벤트 I / O 프레임 워크입니다. IronPython 및 MRI에서 동등한 스크립트를 실행하면 IronPython이 더 빠를 수 있습니다. CPython 및 JRuby + Truffle에서 동등한 스크립트를 실행하면 JRuby + Truffle이 더 빠를 수 있습니다. 이과는 아무 상관이 없습니다 Symbol들하지만, 구현의 품질 : JRuby를 + 트러플이 적극적으로 최적화 컴파일러를 가지고, 플러스 고성능 JVM의 전체 최적화 기계, CPython의 간단한 인터프리터입니다.

루비의 의도 중 하나는 사람이 읽고 쓸 수 있도록하는 것이기 때문에 창작자가 인터프리터 자체에서 다른 언어에서와 같이 개선 된 기능을 구현함으로써 코딩 과정을 쉽게 할 수 없었습니까?

Symbol의 컴파일러 최적화되지 않습니다. 그것들은 특정 의미론을 가진 별도의 데이터 타입입니다. 그들은 YARV처럼하지 flonums 에 대한 개인 내부 최적화하고, Float의. 상황과 동일하지 않다 Integer, Bignum그리고 Fixnum어느 되어야 보이지 전용 내부 최적화 상세하지만, 불행히도이 아니다. (이 마지막으로하는 제거 루비 2.4에서 수정 될 것입니다 FixnumBignum단지와 잎 Integer.)

Java의 특수한 상태로 Java가하는 방식을 수행 String한다는 것은 항상 특수 상태 에 있는지 여부 String와 상황에 따라 자동으로 특정 상태 에 있는지 여부에 대해주의를 기울여야한다는 것을 의미합니다 . 그것은 단순히 별도의 데이터 유형을 갖는 것보다 훨씬 높은 부담입니다.

기호에 대해 언어에 구애받지 않는 정의와 다른 언어로 표시해야하는 이유가 있습니까?

Symbolname 또는 label 의 개념을 나타내는 데이터 유형입니다 . Symbols는 값 객체 이며, 불변이며, 일반적으로 즉시 (언어가 그러한 것을 구별하는 경우) 무국적이며 정체성이 없습니다. 두 Symbol똑같 음도 동일하다는 것이 보장됩니다. 즉, 똑같은 두 Symbols가 실제로는 동일 Symbol합니다. 이는 값 평등과 참조 평등이 동일하므로 평등이 효율적이며 O (1)임을 의미합니다.

언어로 된 이유는 언어와 관계없이 동일합니다. 일부 언어는 다른 언어보다 더 많은 언어를 사용합니다.

예를 들어 Lisp 제품군에는 "가변"이라는 개념이 없습니다. 대신, Symbol값과 관련이 있습니다.

반사 또는 내성적 인 기능을 가진 언어에서 Symbol의 자주 루비, 반사하는 API의 예를 반영 엔티티의 이름을 표시,하는 데 사용됩니다 Object#methods, Object#singleton_methods, Object#public_methods, Object#protected_methods, 및 Object#public_methods반환 ArraySymbol(그들은 단지뿐만 아니라 반환 할 수 있지만들 ArrayMethod들). Object#public_sendA는 소요 Symbol(그것도 허용하지만 인수로 전송하는 메시지의 이름을 나타내는 String뿐만 아니라, Symbol더 정확한 의미이다).

ECMAScript에서 Symbols는 미래에 ECMAScript 기능을 안전하게 만드는 기본 구성 요소입니다. 그들은 또한 성찰에 큰 역할을합니다.


에를 랑 원자는 프롤로그에서 직접 가져 왔습니다 (로버트 비딩 (Robert Virding)는 어느 시점에서 저에게 말했습니다)
Zachary K

2

심볼은 루비에서 유용하며, 각 심볼은 참조 될 때마다 재사용되므로 루비 코드 전체에서 볼 수 있습니다. 변수에 저장되지 않은 문자열을 사용할 때마다 메모리에 새 개체가 만들어 지므로 문자열보다 성능이 향상됩니다. 예를 들어, 동일한 문자열을 해시 키로 여러 번 사용하는 경우 :

my_hash = {"a" => 1, "b" => 2, "c" => 3}
100_000.times { |i| puts my_hash["a"] }

문자열 "a"는 메모리에 101,000 번 생성됩니다. 대신 기호를 사용한 경우 :

my_hash = {a: 1, b: 2, c: 3}
100_000.times { |i| puts my_hash[:a] }

이 심볼 :a은 여전히 ​​메모리에서 하나의 객체입니다. 이것은 문자열보다 기호를 훨씬 더 효율적으로 만듭니다.

업데이트 다음 은 성능 차이를 보여주는 벤치 마크입니다 ( 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
  100_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  100_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

내 MBP에 대한 결과는 다음과 같습니다.

String time: 0.1254125550040044 seconds.
Symbol time: 0.07360960397636518 seconds.

해시에서 키를 식별하기 위해 문자열과 기호를 사용하는 것은 분명한 차이가 있습니다.


이것이 사실인지 확실하지 않습니다. 루비 구현이 반복 할 때마다 코드를 반복해서 분석하지 않고 동일한 코드를 여러 번 실행할 것으로 기대합니다. 어휘의 각 어휘 "a"가 실제로 새로운 문자열 "a"이더라도 , 귀하의 예에는 정확히 두 개가있을 것이라고 생각합니다 (구현물 중 하나가 변경 될 때까지 메모리를 공유 할 수도 있습니다). 수백만 개의 문자열을 만들려면 아마도 String.new ( "a")를 사용해야합니다. 그러나 나는 루비에 정통하지 않아서 아마 틀렸다.
coredump 2012 년

1
Codecademy의 수업 중 하나에서 필자의 예제와 같이 문자열 대 기호에 대한 벤치 마크를 생성합니다. 답변에 추가하겠습니다.
Keith Mattix

1
벤치 마크를 추가해 주셔서 감사합니다. 테스트는 해시 테이블 (신분 대 문자열 비교)에서 더 빠른 테스트로 인해 문자열 대신 기호를 사용하여 얻은 예상 이득을 보여 주지만 각 반복에서 문자열이 할당되는 것을 추론 할 수있는 방법은 없습니다. string_AZ[String.new("r")]차이가 나는지 확인하기 위해 버전을 추가했습니다 . 매번 21ms (원본 버전), 기호가있는 7ms 및 새로운 문자열이있는 50ms를 얻습니다. 따라서 문자열이 리터럴 "r"버전 만큼 할당되지 않았다고 말하고 싶습니다 .
코어 덤프

1
아, 그래서 더 파고 들었고 Ruby 2.1에서는 문자열이 실제로 공유되었습니다. 나는 그 업데이트를 놓쳤다. 지적 해 주셔서 감사합니다. 원래 질문으로 돌아가서 두 벤치 마크 모두 기호 대 문자열의 유틸리티를 보여줍니다.
Keith Mattix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.