Common Lispでラムダの前に# 'が使用されているのはなぜですか?



Why Is Used Before Lambda Common Lisp



解決:

# 'fooはの略語です(関数foo)リーダーによる。

CLには、いくつかの異なる名前空間があります。# 'fooまたは(関数foo)は 機能的価値foo。



複数の名前空間(別名 スロット また 細胞 シンボルの)。

ラムダの場合、CLでは# 'を省略できます。これは、マクロであるため、次のように展開されます(Hyperspecから取得)。



(ラムダラムダリスト[[宣言* |ドキュメント]]フォーム*)==(関数(ラムダラムダリスト[[宣言* |ドキュメント]]フォーム*))==# '(ラムダラムダリスト[[宣言* |ドキュメント]]フォーム*)

# 'はまだ歴史的な理由で使用されている可能性があります(Maclispではラムダは関数形式に拡張されませんでした)、または一部の人々が考えるため、ラムダにシャープクォートでタグを付けると、コードがより読みやすく、一貫性のあるものになる可能性があります。これが違いを生むいくつかの特別な場合があるかもしれませんが、一般的に、どの形式を選択するかは実際には重要ではありません。

私はあなたがそれをこのように考えることができると思います:(function(lambda ...))は関数を返します(ラムダ...)が作成します。ご了承くださいCL Hyperspecのラムダには、マクロとシンボルエントリの両方があります。後者から:

ラムダ式は、特定のコンテキストで関数名の代わりに使用できるリストであり、確立された関数の名前を参照することによって間接的にではなく、その動作を直接記述することによって関数を示します。



のドキュメントから関数:

nameがラムダ式の場合、字句クロージャが返されます。

違いは、次のようなラムダフォームの呼び出しにも関連していると思います。((lambda ...)...)評価対象のフォームとして扱われる場合とvs.(funcall# '(lambda ...)...)。このトピックについてもっと読みたい場合は、c.l.lスレッドがあります。

そのスレッドからのいくつかの引用:

(lambda(x)...それ自体は引用符で囲まれていないリスト構造です。これはFUNCTION特殊形式の引数としての外観です。(function(lambda(x)...これにより関数オブジェクトが存在します

と:

LAMBDAマクロがANSICommon Lispのかなり遅い追加であったという事実によっても複雑になります。そのため、本当に古い人(つまり、私のような)はすべて、ラムダ式に# 'を指定する必要があるときにlispを学習しました。マッピング関数。そうしないと、存在しないラムダ関数が呼び出されます。

マクロの追加によってそれが変わりましたが、私たちの中には、変えたいと思うように設定されすぎている人もいます。


ほとんどの場合、「#」は「ほとんど」不要であり、コードがより冗長になるため、避けるのが最善です。何らかの形式の引用が必要な場合、いくつかの例外があります(以下の例4を参照)。

ノート: この投稿のすべての例はEmacsLisp(GNU Emacs 25.2.1)でテストされていますが、ANSIの一般的なlispでも同じように機能するはずです。基本的な概念は両方の方言で同じです。

簡単な説明
まず、引用を避けるのが最善の場合を考えてみましょう。関数は、それ自体に評価されるファーストクラスのオブジェクトです(たとえば、関数に渡したり変数に割り当てたりする機能など、他のオブジェクトと同じように扱われます)。匿名関数(ラムダ形式など)はそのような例の1つです。 Emacs Lisp(M-x ielm RET)または任意のANSI commonlispで以下を試してください。

((ラムダ(x)(+ x 10))20)-> 30

今、引用されたバージョンを試してみてください

(# '(lambda(x)(+ x 10))20)->'関数エラー 'または'無効な関数... '

# 'の使用を主張する場合は、次のように記述する必要があります。

(funcall# '(lambda(x)(+ x 10))20)-> 30

詳細な説明
引用が必要な場合を本当に理解するには、Lispが式をどのように評価するかを知る必要があります。読む。私はこれを簡潔にすることを約束します。

Lispに関するいくつかの基本的な事実を知る必要があります。

  1. Lisp '常に' 評価します すべての表現。式が引用されていない限り、引用されていない場合は、評価されずに返されます。
  2. 原子 自分自身に評価します。原子式はリストではありません。例としては、数値、文字列、ハッシュテーブル、ベクトルなどがあります。
  3. 記号 (変数名)2種類の値を格納します。それらは通常の値と機能定義を保持できます。したがって、Lispシンボルには、これら2つのタイプを格納するためのセルと呼ばれる2つのスロットがあります。機能しないコンテンツは通常、シンボルの値セルに保持され、機能セルで機能します。非機能的定義と機能的定義の両方を同時に保持する機能により、EmacsLispとCommonLispは2Lispカテゴリに分類されます。 2つのセルのどちらが式で使用されるかは、シンボルの使用方法、より具体的にはリスト内の位置によって異なります。対照的に、Lispの一部の方言(Schemeが最もよく知られている)の記号は、1つの値しか保持できません。スキームには、値セルと機能セルの概念がありません。このようなLispはまとめて1-Lispと呼ばれます。

ここで、LispがS式(括弧で囲まれた式)をどのように評価するかを大まかに理解する必要があります。すべてのS式は、大まかに次のように評価されます。

  1. 引用されている場合は、未評価で返送してください
  2. 引用符で囲まれていない場合は、CAR(最初の要素など)を取得し、次のルールを使用して評価します。

NS。アトムの場合は、単にその値を返します(例:3-> 3、 'pablo'-> 'pablo')
NS。 S式の場合は、同じ全体的な手順を使用して評価します
NS。シンボルの場合、その関数セルの内容を返します

  1. S式のCDRの各要素を評価します(たとえば、リストの最初の要素を除くすべて)。
  2. CARから取得した関数を、CDRの各要素から取得した値に適用します。

上記の手順は、CAR内の任意のシンボルが 引用なし S式は、その関数セルに有効な関数定義を持っている必要があります。

それでは、投稿の最初から例に戻りましょう。なぜ

(# '(ラムダ(x)(+ x 10))20)

エラーを生成しますか?これは、S式のCARである# '(lambda(x)(+ x 10))が、関数引用符#'のためにLispインタープリターによって評価されないためです。

# '(ラムダ(x)(+ x 10))

関数ではありませんが

(ラムダ(x)(+ x 10))

は。引用の目的は評価を防ぐことであることに注意してください。一方、ラムダ形式はそれ自体に評価されます。これは関数形式であり、のCARとして有効です。 引用なし リスト。 LispがのCARを評価するとき

((ラムダ(x)(+ x 10))20)

それが取得します(lambda(x)(+ x 20))、これはリスト内の残りの引数に適用できる関数です(CDRの長さがラムダ式で許可されている引数の数と等しい場合)。したがって、

((ラムダ(x)(+ x 10))20)-> 30

したがって、問題は、関数または関数定義を保持する記号をいつ引用するかです。あなたが「間違って」物事をしない限り、答えはほとんど決してありません。 「誤って」とは、反対のことを行う必要があるときに、シンボルの値セルまたは機能セルに機能定義を配置することを意味します。理解を深めるには、次の例を参照してください。

例1-値セルに格納されている関数
使用する必要があるとします可変数の引数を期待する関数で適用します。そのような例の1つはシンボルです+。 Lispは扱います+通常の記号として。機能定義はに保存されます+の機能セル。使用したい場合は、値セルに値を割り当てることができます

(setq + '私はプラス関数です')。

あなたが評価する場合

+-> '私はプラス関数です'

しかし、(+ 1 2)は引き続き期待どおりに機能します。

(+ 1 2)-> 3

関数applyは、再帰で非常に役立ちます。リスト内のすべての要素を合計するとします。書くことはできません

(+ '(1 2 3))->間違ったタイプ..。

その理由は、+はその引数が数値であることを期待しているためです。適用はこの問題を解決します

(適用# '+'(1 2 3))->(+ 1 2 3)-> 6

+上記を引用したのはなぜですか?上で概説した評価ルールを覚えておいてください。 Lispは、関数セルに格納されている値を取得することにより、シンボルapplyを評価します。引数のリストに適用できる機能プロシージャを取得します。しかし、私が引用しない場合+、LispはS式の最初の要素ではないため、値セルに格納されている値を取得します。設定したので+の値セルから「私はプラス関数です」Lispは+の関数セルに保持されている関数定義を取得しません。実際、値セルを「私はプラス関数です」に設定しなかった場合、Lispはapplyの要求に応じて、関数ではないnilを取得します。

使用する方法はありますか+適用で引用されていませんか?はいあります。次のコードを評価するだけです。

(setq +(symbol-function '+))(apply +'(1 2 3))

これはに評価されます6、Lispが評価するので予想通り(apply + '(1 2 3))+の値セルに格納されている+の機能定義を見つけるようになりました。

例2-バリューセルへの機能定義の保存
シンボルの値セルに機能定義を格納するとします。これは次のように達成されます。

(setq AFunc(lambda(x)(* 10 x)))

評価中

(AFunc 2)

Lispがで関数を見つけることができないため、エラーを生成しますAFuncの関数セル。これを回避するには、funcallを使用します。これは、シンボルの値セルの値を機能定義として使用するようにLispに指示します。これは「funcall」を使用して行います。

(funcall AFunc 2)

シンボルの値セルに格納されている機能定義が有効であると仮定すると、

(funcall AFunc 2)-> 20

あなたは使用することを避けることができますを使用してシンボルの関数セルにラムダ形式を配置することによるfuncallfset:

(fset'AFunc(lambda(x)(* 10 x)))(AFunc 2)

このコードブロックは20lispはで機能的な定義を見つけるのでAFuncの関数セル。

例3-ローカル関数
関数を作成していて、他の場所では使用されない関数が必要だとします。典型的な解決策は、メイン関数のスコープ内でのみ有効な関数を定義することです。これを試して:

(defun SquareNumberList(AListOfIntegers) '不要なローカル関数を持つ愚かな関数。'(let((Square(lambda(ANumber)(* ANumber ANumber))))(mapcar Square AListOfIntegers)))(SquareNumberList '(1 2 3) )

このコードブロックは

(1 4 9)

上記の例でSquareが引用されていない理由は、S式が上記で概説したルールに従って評価されるためです。まず、Lispはの機能定義を引き出しますmapcar。次に、Lispは2番目の引数の(例: 'Square)値セルの内容をプルします。最後に、それは戻ります(1 2 3)3番目の引数については未評価。

例4-シンボルの値セルと関数セルの内容
引用符が必要な場合の1つを次に示します。

(setq ASymbol'Symbol's Value ')(fset'ASymbol(lambda()' Symbol's Function '))(progn(print(format' Symbol's value->%s '(symbol-value' ASymbol)))(print(format 'シンボルの関数->%s '(シンボル関数' ASymbol)))))

上記のコードは次のように評価されます

'シンボルの値->シンボルの値' 'シンボルの関数->(lambda nilシンボルの関数)' nil

見積もりが必要です

(fset'ASymbol(lambda() 'Symbol's Function'))

(シンボル値 'ASymbol)

(シンボル関数 'ASymbol)

そうでなければ、Lispはそれぞれの場合にASymbolの値を取得し、fset、symbol-value、およびsymbol-functionが正しく機能しない。

この長い投稿が誰かに役立つことを願っています。