Rubyクリエイターがシンボルの概念を使用することを選んだのはなぜですか?


15

tl; dr:シンボルの言語に依存しない定義と、他の言語でそれらを使用する理由はありますか?

では、なぜRubyの作成者symbolsは言語の概念を使用したのですか?

これは、ルビーではないプログラマーの観点からお願いします。私は他の多くの言語を学びましたが、どれにもRubyが呼んでいるものを扱っているかどうかを指定する必要があることを知りませんでしたsymbols

主な質問は、symbolsRuby の概念はパフォーマンスのために存在するのか、それとも言語の記述方法のために必要なものなのか、ということです。

Rubyのプログラムは、PythonやJavascriptの対応物よりも軽量または高速、あるいはその両方でしょうか?もしそうなら、それは原因symbolsでしょうか?

Rubyの目的の1つは人間が読み書きしやすいことであるため、作成者はインタープリター自体に(他の言語の場合と同様に)これらの改善を実装することでコーディングのプロセスを緩和できませんでしたか?

symbolsそもそもなぜ彼らがそこにいるのかではなく、何がどのように使われているのかを誰もが知りたいようです。


Scalaには私の頭上にシンボルがあります。多くのLispがそうだと思います。
D.ベンノーブル

回答:


17

Rubyの作成者であるYukihiro "Matz" Matsumotoは、RubyがLisp、Smalltalk、Perl(およびWikipediaによるAdaとEiffelによる影響)についての説明を投稿しました

Rubyは、次の手順で設計された言語です。

  • 単純なLisp言語(CL以前の言語など)を使用します。
  • マクロ、s-expressionを削除します。
  • 単純なオブジェクトシステムを追加します(CLOSよりもはるかに単純です)。
  • 高次関数に触発されたブロックを追加します。
  • Smalltalkにあるメソッドを追加します。
  • Perlにある機能を(OOの方法で)追加します。

したがって、Rubyは理論的にはもともとLispでした。

これからMatzLispと呼びましょう。;-)

どのコンパイラでも、関数、変数、名前付きブロック、型などの識別子を管理します。通常、デバッグ情報を追加する場合を除き、それらをコンパイラに保存し、生成された実行可能ファイルではそれらを忘れます。

Lispでは、そのようなシンボルは一流のリソースであり、異なるパッケージでホストされます。つまり、実行時に新しいシンボルを追加し、それらを異なる種類のオブジェクトにバインドできます。これは、コードの他の部分と名前の衝突がないことを確認できるため、メタプログラミングの際に役立ちます。

また、シンボルは読み取り時にインターンされ、IDによって比較できます。これは、新しい種類の(数値のように抽象的)を持つ効率的な方法です。これにより、整数で裏付けられた独自の列挙型を定義する代わりに、シンボリック値を直接使用するコードを記述できます。また、各シンボルは追加のデータを保持できます。これが、たとえば、Emacs / SlimeがEmacsからのメタデータをシンボルのプロパティリストに添付する方法です。

シンボルの概念はLispの中心です。詳細な例については、PAIP(Paradigms of Artificial Intelligence Programming:Case Studies in Common Lisp、Norvig)の例をご覧ください


5
いい答えです。しかし、私はMatzに同意しません。マクロなしの言語をLisp方言と呼ぶことは考えません。lispのランタイムメタプログラミング機能は、まさにこの言語に素晴らしい力を与え、非常に単純で表現力に欠ける文法を補うものです。
cmaster-モニカの復元16年

11

では、なぜRubyの作成者はsymbols言語の概念を使用しなければならなかったのですか?

まあ、彼らは厳密に「しなければならない」のではなく、彼らが選んだのです。また、厳密に言えばSymbolsは言語の一部ではなく、コアライブラリの一部であることに注意してください。これらに言語レベルのリテラル構文がありますが、を呼び出して作成する必要がある場合でも同様に機能しますSymbol::new

私はそれを理解しようとしている非ルービーなプログラマーの観点から尋ねます。私は他の言語をたくさん学びましたが、Rubyが呼ぶものを扱っているかどうかを指定する必要性がそれらのどれにもありませんでしたsymbols

これらの「他の多くの言語」が何であるかは言わなかったが、SymbolRubyのようなデータ型を持つ言語のほんの一部を次に示します。

Symbols の機能を異なる形式で提供する他の言語もあります。Javaでは、例えば、Rubyのの機能String:sが2つ(実際には3つ)のタイプに分けられるStringStringBuilder/ StringBuffer。一方、Rubyのの機能Symbolタイプは、Javaに折り畳まれているStringタイプ:JavaのStringsができインターンリテラル文字列Stringコンパイル時の結果は定数式を評価さsは自動的に抑留され、動的に生成さStringsが呼び出すことでインターンすることができますString.intern方法。StringJava のインターンはSymbolRuby のインターンとまったく同じですが、別個のタイプとして実装されるのではなく、JavaがString(注:以前のバージョンのRubyではString#to_sym呼び出されていましたがString#intern、そのメソッドは今日でもレガシーエイリアスとして存在しています。)

主な質問は次のとおりです。Rubyの概念はsymbols、それ自体および他の言語に対するパフォーマンスの意図として存在しますか、

Symbolsは何よりもまず、特定のセマンティクスを持つデータ型です。これらのセマンティクスにより、いくつかのパフォーマンス操作(たとえば、高速O(1)同等性テスト)を実装することもできますが、それは主な目的ではありません。

または言語の記述方法のために存在するために必要なものだけですか?

SymbolsはRuby言語ではまったく必要ありません。Rubyはそれらがなくても問題なく動作します。これらは純粋にライブラリ機能です。Symbolsに関連付けられている言語には、正確に1つの場所があります。defメソッド定義式Symbolは、定義されているメソッドの名前を示すものとして評価されます。ただし、それはかなり最近の変更であり、それ以前は、戻り値は未指定のままでした。MRIは単純に評価されnil、RubiniusはRubinius::CompiledMethodオブジェクトに評価されます。UnboundMethod…または単に評価することも可能Stringです。

Rubyのプログラムは、PythonやNodeの対応物よりも軽量または高速、あるいはその両方ですか?もしそうなら、それは原因symbolsでしょうか?

ここで何を聞いているのかわかりません。パフォーマンスは、ほとんどの場合、言語ではなく実装品質の問題です。さらに、Nodeは言語ではなく、ECMAScriptのイベントI / Oフレームワークです。IronPythonとMRIで同等のスクリプトを実行すると、IronPythonの方が高速になる可能性があります。CPythonおよびJRuby + Truffleで同等のスクリプトを実行すると、JRuby + Truffleの方が高速になる可能性があります。これはSymbols とは関係ありませんが、実装の品質とは関係ありません。JRuby+ Truffleには積極的に最適化するコンパイラーと、高性能JVMの最適化機構全体があり、CPythonはシンプルなインタープリターです。

Rubyの意図の1つは人間が簡単に読み書きできるようにすることであるため、その作成者は(他の言語の場合と同様に)インタープリター自体にそれらの改善を実装することでコーディングのプロセスを緩和できませんか?

いいえSymbol。sはコンパイラの最適化ではありません。これらは、特定のセマンティクスを持つ個別のデータ型です。これらはsのプライベート内部最適化であるYARVのflonumsとは異なりFloatます。状況はIntegerBignumおよびの場合と同じではありません。Fixnumこれら、目に見えないプライベートな内部最適化の詳細であるべきですが、残念ながらそうではありません。(これは最終的に削除され、ルビー2.4で修正される予定ですFixnumし、Bignumただ、葉Integer。)

通常Stringのsの特別な状態としてJavaが行うようにそれを行うことは、自分Stringのsがその特別な状態にあるかどうか、どのような状況で自動的に特別な状態にあるか、そうでないかについて常に注意する必要があることを意味します。これは、単純に別個のデータ型を持つよりもはるかに大きな負担です。

シンボルの言語に依存しない定義と、それらを他の言語で使用する理由はありますか?

Symbolは、名前またはラベルの概念を示すデータ型ですSymbolsは値オブジェクトであり、不変、通常即時(言語がそのようなことを区別する場合)、ステートレスであり、アイデンティティを持ちません。Symbol等しい2つの値も同一であることが保証されています。言い換えると、Symbol等しい2つの値は実際には同じものSymbolです。つまり、値の等価性と参照の等価性は同じものであり、したがって等価性は効率的でO(1)です。

言語でそれらを使用する理由は、言語に関係なく、本当に同じです。一部の言語は、他の言語よりもそれらに依存しています。

たとえば、Lispファミリでは、「変数」という概念はありません。代わりに、Symbol値に関連付けられたがあります。

反射又は内省機能を持つ言語で、SymbolsはしばしばリフレクションAPIにおける反射エンティティの名前を示すために使用され、例えばルビーで、Object#methodsObject#singleton_methodsObject#public_methodsObject#protected_methods、およびObject#public_methods戻りArraySymbol(彼らは同様に返す可能性が秒ArrayMethod秒)。Object#public_sendかかるSymbol(それはまた、受け入れが引数として送信するメッセージの名前を示すStringだけでなく、Symbolより意味的に正確です)。

ECMAScriptでは、Symbolsは将来ECMAScriptの機能を安全にするための基本的な構成要素です。また、リフレクションにも大きな役割を果たします。


Erlangの原子がプロローグから直接採取された(ロバート・Virdingは、そのいくつかの点で私に言った)
ザカリーK

2

シンボルはRubyで役立ちます。各シンボルは参照されるたびに再利用されるため、Rubyコード全体に表示されます。これは、変数に保存されていない文字列を使用するたびにメモリ内に新しいオブジェクトが作成されるため、文字列よりもパフォーマンスが向上します。たとえば、同じ文字列をハッシュキーとして複数回使用する場合:

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はまだメモリ内の1つのオブジェクトです。これにより、文字列よりもシンボルの効率が大幅に向上します。

更新パフォーマンスの違いを示す ベンチマーク(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.

ハッシュ内のキーを識別するためだけに文字列とシンボルを使用することには明確な違いがあります。


これが事実かどうかはわかりません。Rubyの実装では、繰り返しごとにコードを何度も解析するのではなく、同じコードを複数回実行することを期待しています。の各レキシカルオカレンス"a"が実際に新しい文字列である場合でも、あなたの例では正確に2つあると思います"a"(そして、実装の1つが変更されるまでメモリを共有することさえできます)。数百万の文字列を作成するには、おそらくString.new( "a")を使用する必要があります。しかし、私はRubyに精通していないので、多分間違っています。
コアダンプ

1
Codecademyのレッスンの1つでは、私の例のように、文字列とシンボルのベンチマークを生成します。答えに追加します。
キースマティックス16

1
ベンチマークを追加していただきありがとうございます。ハッシュテーブルでのテストが高速であるため、テストでは文字列の代わりにシンボルを使用して得られた期待されるゲインが表示されます(同一性と文字列の比較)。string_AZ[String.new("r")]違いがあるかどうかを確認するために、バージョンを追加しました 。文字列(元のバージョン)で21ミリ秒、シンボルで7ミリ秒、新鮮な文字列で50ミリ秒を取得します。したがって、文字列はリテラル"r"バージョンではあまり割り当てられていません。
コアダンプ

1
ああ、それでもう少し掘り下げました。Ruby2.1では、文字列は実際に共有されています。その更新を見逃したようです。それを指摘してくれてありがとう。元の質問に戻って、両方のベンチマークがシンボル対文字列の有用性を示していると思います。
キースマティックス16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.