Clojureに「記号」に加えて「キーワード」があるのはなぜですか?


130

私はずっと前から、他のLisp(特にScheme)についての知識を持っています。最近、私はClojureについて読んでいます。「記号」と「キーワード」の両方があることがわかります。私がよく知っているが、キーワードではない記号。

他のLispにはキーワードがありますか?キーワードは、表記が異なる(つまり、コロン)以外の記号とどのように異なりますか?


回答:


139

ここだClojureの文書のキーワードと記号については。

キーワードは、それ自体を評価する記号識別子です。彼らは非常に高速な同等性テストを提供します...

シンボルは、他の何かを参照するために通常使用される識別子です。これらはプログラム形式で使用して、関数パラメーターを参照したり、バインディング、クラス名、グローバル変数を使用したりできます...

キーワードは一般に、軽量の「定数文字列」として使用されます。たとえば、ハッシュマップのキーやマルチメソッドのディスパッチ値などです。シンボルは通常、変数と関数に名前を付けるために使用され、マクロなどを除いて、それらをオブジェクトとして直接操作することはあまり一般的ではありません。しかし、キーワードを使用するすべての場所でシンボルを使用することを妨げるものは何もありません(常に引用することを気にしなければ)。

違いを確認する最も簡単な方法は、読むことですKeyword.javaし、Symbol.javaClojureのソースで。いくつかの明らかな実装の違いがあります。たとえば、Clojureのシンボルはメタデータを持つことができ、キーワードは持つことができません。

シングルコロン構文に加えて、ダブルコロンを使用して名前空間修飾キーワードを作成できます。

user> :foo
:foo
user> ::foo
:user/foo

Common Lispにはキーワードがあり、Rubyや他の言語と同じです。もちろん、これらの言語では少し異なります。Common LispキーワードとClojureキーワードのいくつかの違い:

  1. Clojureのキーワードはシンボルではありません。

    user> (symbol? :foo)  
    false
  2. 特に限定しない限り、キーワードはどの名前空間にも属しません。

    user> (namespace :foo)
    nil
    user> (namespace ::foo)
    "user"

(見るもののアイデアをくれてくれたRainer Joswigに感謝します。)


10
これは違いが何であるかを説明しますが、2つの異なる構成が必要な理由は説明しません。Clojureは、KeywordとSymbolの両方の機能を統合したものを作成できませんでしたか?

25
キーワードは軽量であり、便利な構文を持っています。これですべてです。言語がなくても問題なく動作しますが、使いやすく、非常に広く使用されています。キーワードは常に自己評価しているため(つまり、変数名や関数名として使用できません)、一般にシンボルは常に自己評価しているわけではないため、それらの能力を統合することはできません。
ブライアンカーパー、

1
キーワードは、評価後に変更されないため、ハッシュマップなどのキーとしてより有用であるようです:(eval (eval ':a))vs (eval (eval ''a))。他に利点はありますか?パフォーマンスに関しては、それらは同じですか?
kristianlm 2012

5
(同一?:qwe:qwe)-> true。(同一? 'qwe' qwe)-> false。シンボルは内部でインターンされた文字列を使用するため、比較も高速です。
desudesudesu 2014年

29

Common Lispにはキーワード記号があります。

キーワードも記号です。

(symbolp ':foo) -> T

キーワードが特別な理由:

  • :fooはCommon Lispリーダーによってシンボルキーワードとして解析されます:: foo
  • キーワード自体に評価される::foo->:foo
  • キーワードシンボルのホームパッケージはKEYWORDパッケージです:keyword:foo->:foo
  • キーワードはパッケージKEYWORDからエクスポートされます
  • キーワードは定数です。別の値を割り当てることはできません

それ以外の場合、キーワードは通常の記号です。したがって、キーワードは関数に名前を付けたり、プロパティリストを持つことができます。

注意:Common Lispでは、シンボルはパッケージに属しています。これは次のように書くことができます:

  • foo、現在のパッケージでシンボルにアクセスできる場合
  • foo:bar、シンボルFOOがパッケージBARからエクスポートされるとき
  • foo :: bar、シンボルFOOがパッケージBARにある場合

:foo、keyword:fooおよびkeyword :: fooがすべて同じシンボルであることを意味するキーワードシンボルの場合。したがって、後者の2つの表記は通常使用されません。

したがって、シンボル名の前にパッケージ名を指定しないと、デフォルトではKEYWORDパッケージが使用されると想定して、:fooは解析されてパッケージKEYWORDに含まれるようになります。


6

キーワードはそれ自体が評価する記号なので、引用することを覚えておく必要はありません。


5
それですか?タイピング: 'ではなく、大きな勝利のようには見えません。
ローレンスゴンサルベス

11
まあ、本当にそれは単なるキャラクター以上のものです。キーワードは評価後もキーワードのままですが、シンボルはバインドされたものに評価されます。これらは通常、異なる目的で使用されるため、セマンティックの違いに似ています。
グレッグヒューギル

13
Clojureではキーワードはシンボルではありません
David Plumpton、

4

:keywordsも多くのコレクションで特別に扱われるため、非常に便利な構文を使用できます。

(:user-id (get-users-map))

と同じです

((get-users-map) :user-id)

これは少し物事をより柔軟にします


21
これはまた、 '({1 'B 2}' B( '{' 1 => 1)2 B})'、シンボルにも当てはまる=> 2
ジョナス

4

キーワードの場合、ハッシュ値は、キーワードが最初に作成されたときに計算され、キャッシュされます。キーワードをハッシュキーとして検索する場合は、事前に計算されたハッシュ値を返すだけです。文字列とシンボルの場合、ハッシュはルックアップごとに再計算されます。

同じ名前のキーワードが常に同一である理由、それらは独自のハッシュ値を含んでいます。マップおよびセットの検索はハッシュキーから行われるため、検索自体ではなく、多数の検索を行う場合の検索効率が向上します。


0

キーワードはグローバルですが、シンボルはグローバルではありません

この例はJavaScriptで記述されていますが、要点を伝えるのに役立つことを願っています。

const foo = Symbol.for(":foo") // this will create a keyword
const foo2 = Symbol.for(":foo") // this will return the same keyword
const foo3 = Symbol(":foo") // this will create a new symbol
foo === foo2 // true
foo2 === foo3 // false

Symbol関数を使用してシンボルを作成すると、毎回個別/プライベートシンボルが取得されます。Symbol.for関数を介してシンボルを要求すると、毎回同じシンボルが返されます。

(println :foo) ; Clojure
System.out.println(RT.keyword(null, "foo")) // Java
console.log(System.for(":foo")) // JavaScript

これらはすべて同じです。


関数の引数名はローカルです。つまり、キーワードではありません。

(def foo (fn [x] (println x))) ; x is a symbol
(def bar (fn [x] (println x))) ; not the same x (different symbol)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.