19

問題はキーワードの使用ではなく、実際にはキーワードの実装についてです。たとえば、キーワード パラメーターを使用して関数を作成し、呼び出しを行うと、次のようになります。

(defun fun (&key key-param) (print key-param)) => FUN
(find-symbol "KEY-PARAM" 'keyword) => NIL, NIL   ;;keyword is not still registered
(fun :key-param 1) => 1
(find-symbol "KEY-PARAM" 'keyword) => :KEY-PARAM, :EXTERNAL

引数を渡すためにキーワードをどのように使用しますか? キーワードは、それ自体が値であるシンボルです。では、キーワードを使用して対応するパラメーターをバインドするにはどうすればよいでしょうか?

キーワードに関する別の質問 — キーワードはパッケージを定義するために使用されます。既存のキーワードで名前を付けたパッケージを定義できます。

(defpackage :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ...
(in-package :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ...
(defun fun (&key key-param) (print key-param)) => FUN
(fun :KEY-PARAM 1) => 1

:KEY-PARAMシステムは、パッケージ名と関数パラメーター名の使用をどのように区別しますか? また、関数を定義KEY-PARAMしてエクスポートすると、より複雑なものを作成できます (実際には関数ではなく名前です)。

(in-package :KEY-PARAM)
(defun KEY-PARAM (&key KEY-PARAM) KEY-PARAM) => KEY-PARAM
(defpackage :KEY-PARAM (:export :KEY-PARAM))  
   ;;exporting function KEY-PARAM, :KEY-PARAM keyword is used for it
(in-package :CL-USER) => #<The COMMON-LISP-USER package, ...
(KEY-PARAM:KEY-PARAM :KEY-PARAM 1) => 1
   ;;calling a function KEY-PARAM from :KEY-PARAM package with :KEY-PARAM parameter...

質問は同じです。Common Lisp は:KEY-PARAMここでキーワードの使用法をどのように区別しますか?

Common Lisp のキーワードについて、メカニズムの説明が記載されたマニュアルがあれば、ここにリンクを貼っていただければ幸いです。キーワードの使用法だけを扱った短い記事しか見つからないからです。

4

3 に答える 3

11

キーワード パラメータの詳細については、Common Lisp Hyperspecを参照してください。注意してください

(defun fun (&key key-param) ...)

実際には次の略です:

(defun fun (&key ((:key-param key-param)) ) ...)

キーワード パラメータの完全な構文は次のとおりです。

((keyword-name var) default-value supplied-p-var)

default-valueおよびsupplied-p-varオプションです。としてキーワード記号を使用するのが慣例keyword-nameですが、必須ではありません。varの代わりにa を指定すると(keyword-name var)、デフォルトkeyword-nameで、キーワード パッケージ内の と同じ名前のシンボルになりますvar

たとえば、次のようにします。

(defun fun2 (&key ((myoption var))) (print var))

そしてそれを次のように呼び出します:

(fun 'myoption 3)

内部で機能する方法は、関数が呼び出されているときであり、引数リストをステップスルーして、引数のペアを収集します<label, value>。各 についてlabel、その を持つパラメータのパラメータ リストを検索し、対応する をkeyword-nameバインドします。varvalue

通常、キーワードを使用する理由は、:プレフィックスが目立つためです。そして、これらの変数は自己評価するようになっているので、それらをクォートする必要もありません。つまり、:key-param代わりに':key-param(参考までに、この後者の表記は以前の Lisp システムでは必要でしたが、CL の設計者はそれが醜いと判断し、冗長)。また、変数とは異なる名前のキーワードを指定する機能は、混乱を招くため、通常は使用しません。これは、完全な一般性のためにこのように行われました。また、キーワードの代わりに通常のシンボルを許可することは、引数リストがマージされ、競合を回避したい CLOS のような機能に役立ちます。ジェネリック関数を拡張している場合は、キーワードが独自のパッケージにあり、そこにあるパラメーターを追加できます。衝突しません。

パッケージを定義して変数をエクスポートするときのキーワード引数の使用は、単なる慣習です。 DEFPACKAGEIN-PACKAGEEXPORTなどは、与えられた名前のみを気にし、それがどのパッケージに含まれているかは気にしません。次のように書くことができます

(defpackage key-param)

通常は同様に機能します。多くのプログラマーがこれを行わない理由は、独自のパッケージにシンボルをインターンするためです。これにより、別のパッケージからインポートしようとしているシンボルと同じ名前が発生した場合、パッケージの競合が発生することがあります。キーワードを使用すると、これらのパラメーターがアプリケーションのパッケージから分離され、このような潜在的な問題が回避されます。

肝心なのは、シンボルを使用していて、そのアイデンティティーではなく名前だけに関心がある場合は、多くの場合、キーワードを使用するのが最も安全です。

最後に、さまざまな方法で使用される場合のキーワードの区別について。キーワードは単なる記号です。関数またはマクロが通常のパラメーターを期待する場所で使用された場合、そのパラメーターの値はシンボルになります。引数を持つ関数を呼び出す場合&key、引数をパラメーターに関連付けるためのラベルとしてそれらが使用されるのはそのときだけです。

于 2012-12-25T08:01:45.647 に答える
4

良いマニュアルはPCL の Chapter 21 です

質問に簡単に答える:

  • キーワードはパッケージでエクスポートされたシンボルであるため、 としてだけでなく、keyword:akeyword:a

  • 関数の引数リスト (s と呼ばれる) のキーワードlambda-listは、おそらく次のように実装されます。&key修飾子が存在する場合、lambdaフォームは次のようなものに展開されます。

    (let ((key-param (getf args :key-param)))
      body)
    
  • キーワードを使用してパッケージに名前を付けると、実際には として使用されますstring-designator。これは、後でシンボルとして使用される文字列を処理する特定の関数に渡すことを可能にする Lisp の概念です (パッケージ、クラス、関数などのさまざまな名前)。文字列だけでなく、キーワードやシンボルも. したがって、パッケージを定義/使用する基本的な方法は、実際には次のとおりです。

    (defpackage "KEY-PARAM" ...)
    

    しかし、次のものも使用できます。

    (defpackage :key-param ...)
    

    (defpackage #:key-param ...)
    

    (#:これはインターンされていないシンボルを作成するためのリーダー マクロです。プロセスで不要なキーワードを作成しないため、この方法が推奨されます)。

    後の 2 つの形式は、大文字の文字列に変換されます。したがって、キーワードはキーワードのままであり、パッケージはそのキーワードから変換された文字列として名前を取得します。

要約すると、キーワードには、他のシンボルと同様に、それ自体の価値があります。keyword違いは、キーワードがpackage またはその明示的な使用法による明示的な修飾を必要としないことです。また、他のシンボルと同様に、オブジェクトの名前として使用できます。たとえば、キーワードを使用して関数に名前を付けることができ、すべてのパッケージで「魔法のように」アクセスできます:) 詳細については、@Xach のブログ投稿を参照してください。

于 2012-12-25T08:05:19.830 に答える
1

キーワードのさまざまな用途を区別する「システム」は必要ありません。それらは単に名前として使用されます。たとえば、2 つの plist があるとします。

(defparameter *language-scores* '(:basic 0 :common-lisp 5 :python 3))
(defparameter *price* '(:basic 100 :fancy 500))

言語のスコアを生成する関数:

(defun language-score (language &optional (language-scores *language-scores*))
  (getf language-scores language))

キーワードを とともに使用するとlanguage-score、さまざまなプログラミング言語が指定されます。

CL-USER> (language-score :common-lisp)
5

では、システムは のキーワードと のキーワードを区別するために何を行うの*language-scores*でしょ*price*うか? 何もない。キーワードは単なる名前であり、さまざまなデータ構造でさまざまなものを指定します。それらは、自然言語における同音異義語と同じように区別されません。それらの使用は、特定のコンテキストでの意味を決定します。

上記の例では、間違ったコンテキストで関数を使用することを妨げるものは何もありません:

(language-score :basic *prices*)
100

この言語は、私たちがこれを行うのを妨げるものではありませんでした。それほど派手ではないプログラミング言語とそれほど派手ではない製品のキーワードはまったく同じです。

これを防ぐには多くの可能性があります: オプションの引数 forlanguage-scoreを最初から許可しない、パッケージを外部化せずに別のパッケージに入れる*prices*、グローバルな特殊を使用する代わりにレキシカル バインディングを閉じ、*language-scores*エントリを追加および取得する手段のみを公開する。おそらく、コードベースや慣習を理解しているだけで、それを防ぐのに十分です。ポイントは、システムがキーワード自体を区別することは、実装したいことを達成するために必要ではないということです。

あなたが尋ねる特定のキーワードの使用に違いはありません。実装は、キーワード引数のバインディングをalist、plist、hash-tableなどに格納する場合があります。パッケージ名の場合、キーワードはパッケージ指定子として使用され、代わりにパッケージ名が文字列 (大文字) として使用される場合があります。実装が文字列をキーワードに変換するか、キーワードを文字列に変換するか、または内部でまったく異なるものに変換するかは、実際には問題ではありません。重要なのは名前と、それがどのコンテキストで使用されるかだけです。

于 2012-12-25T08:03:57.513 に答える