2

Doug Hoyte の本「Let Over Lambda」を読んでいると、次の#.記号の説明、別名 read-macroを見つけました。

COMMON LISP に組み込まれている基本的な読み取りマクロは #. 読み取り時評価マクロ。この read マクロを使用すると、シリアル化できないが、少しの Lisp コードで作成できる、読み取るフォームにオブジェクトを埋め込むことができます。

これは第 4 章からのもので、本の大部分はここにあります: http://letoverlambda.com/index.cl/toc

これは、同じ式が毎回異なって読まれる可能性があることを示す本からの例です。

* '(football-game
     (game-started-at
       #.(get-internal-real-time))
     (coin-flip
       #.(if (zerop (random 2)) 'heads 'tails)))

(FOOTBALL-GAME
  (GAME-STARTED-AT 187)
  (COIN-FLIP HEADS))

* '(football-game
     (game-started-at
       #.(get-internal-real-time))
     (coin-flip
   #.(if (zerop (random 2)) 'heads 'tails)))

(FOOTBALL-GAME
  (GAME-STARTED-AT 309)
  (COIN-FLIP TAILS))

次に、著者はいくつかのハードコアなトリックを実演し、#マクロでバリエーションを作成します。

#'つまり、これもある種の読み取りマクロであることが判明し、通常は関数の名前を表すシンボルの前に使用されます。しかし、それは必要であり、そこでの彼の仕事は正確には何ですか?

高階関数のシンボルは、それの#'有無にかかわらず配置できます。

CL-USER> (defun test nil t)
TEST
CL-USER> (funcall #'test)
T
CL-USER> (funcall 'test)
T

同じ成功を収めました。

4

4 に答える 4

7

ほとんどの言語とは異なり、Common Lisp には実際にはパーサーがありません。リーダーと呼ばれるレクサーがあります。リーダーは単一の文字を消費し、それらをテーブルで検索し、そこにある関数を呼び出します[1]。他の言語でパーサーが果たす役割は、Lisp ではマクロによって果たされます。

[1] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_b.htm

たとえば、セミコロンのリーダーは行の残りを消費し、コメントとして破棄します。たとえば、開いた括弧のリーダーです。リストの要素を再帰的に読み取る関数を呼び出します。たとえば、単一引用符は単一のフォームを再帰的に読み取り、それを引用符で囲みます。したがって、'(1 2 3) は (quote (1 2 3)) と読みます。これらの主要な複雑なトークン リーダーは、ほんの一握りです。

[2] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_d.htm

キャラクター#\#は、多くの追加の読者の行動を配置する場所を提供します。ハッシュのリーダーは、メイン リーダーの設計を繰り返します。別の文字を消費し、それをテーブルで調べて、そこにある関数を呼び出します。これらはたくさん[3]あります。

[3] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_dh.htm

したがって、たとえば、代わりにベクトルを読み取るリスト用のリーダーと同様のリーダーがあります (例: #(1 2 3))。#\;したがって、たとえば、単一のセミコロン、二重引用符、またはピリオドをそれぞれ、#\"、として入力できる単一文字のリーダーがあり#\. ます。

特定の質問に答えるには: #'foo などの quote のハッシュ リーダーは、通常の quote のハッシュ リーダーと似ています。次のトークンを読み取り、関数でラップします。#'foo は (関数 foo) と読みます。

リーダーのテーブルを変更して、言語をカスタマイズすることができます。テーブル内のエントリは、リーダー マクロと呼ばれます。defmacro で定義されたマクロとはかなり異なるため、人々をやや混乱させがちな名前です。これらは共に、言語を「成長させる」能力と呼ばれるものを提供します[4]。

[4] http://www.catonmat.net/blog/growing-a-language-by-guy-steele/

于 2014-06-01T13:10:51.840 に答える
3

関数指定子として #'foo と 'foo を使用する場合のもう 1 つの違いは、#'foo は関数オブジェクトとして評価されますが、'foo はシンボルとして評価されることです。したがって、'foo を使用すると、関数オブジェクトを見つける作業が後回しになります。これを 1 回だけではなく、ループのすべてのサイクルで実行すると、パフォーマンスが大幅に低下する可能性があります。

CL-USER> (defun foo () 42)
FOO
CL-USER> (read-from-string "'foo")
=> (QUOTE FOO), 4

CL-USER> (eval *)
FOO
CL-USER> (read-from-string "#'foo")
=> (FUNCTION FOO), 5

CL-USER> (eval *)
=> #<FUNCTION FOO>
于 2014-06-01T20:55:23.470 に答える
0

この経験から、多くのパーツで構成される大きなシステムでは、"'" と "#'" のイディオムを使用すると、パッチ適用が容易になることがわかりました。その理由は、シンボルに関連付けられた関数オブジェクトが検出されるたびに検索され、これがすべての環境で検索されるためです。新しい定義 (ほとんどの場合、パッチ) を (もちろん対話的に) ロードするとすぐに、次に検出されたときに使用されます。パフォーマンス コストは非常に小さいですが、柔軟性の利点は非常に大きいです。アプリケーションを再試行するときのお客様の「うわー、動くようになった!」という顔を想像するのは本当にいいことです。:-)

于 2017-11-17T17:10:06.167 に答える