サーチ…


グループ化はいつ必要ですか?

Common Lispのいくつかの場所では、一連のフォームが順番に評価されます。例えば、 defunまたはlambdaの本体、または点線の本体でそのような場合、複数の書式を順番に書くことは、期待通りに機能します。しかし、 if式のthenelse部分のようないくつかの場所では、単一の形式しか許されません。もちろん、それらの場所で複数の式を実際に評価したいかもしれません。そのような状況では、暗黙的な明示的なグループ化形式が必要です。

Progn

汎用特殊演算子prognは、0個以上のフォームを評価するために使用されます。最後のフォームの値が返されます。例えば、以下では、 (print 'hello)が評価され(その結果は無視されます)、 42が評価され、その結果( 42 )が返されます:

(progn 
  (print 'hello)
  42)
;=> 42

progn内に書式がない場合、 nilが返されます。

(progn)
;=> NIL

prognは、一連のフォームをグループ化するだけでなく、 prognフォームが最上位フォームの場合、その中のすべてのフォームが最上位フォームとして処理されるという重要な特性も備えています。これは、複数のフォームに展開され、すべてが最上位フォームとして処理されるマクロを記述するときに重要になります。

Prognは、最後のフォームのすべての値を返すという点でも有益です。例えば、

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

対照的に、いくつかのグループ化式は結果生成フォームの主な値のみを返します。

暗黙の予言

一部の形式では、 暗黙的な予測を使用してその動作を記述します。例えば、 whenおよびunlessフォームは暗黙のprognの面で彼らの振る舞いを記述場合は 、基本的に偏っているマクロ。これは、

(when (foo-p foo)
  form1
  form2)

が評価され、条件(foo-p foo)が真の場合、 form1form2はあたかもprogn内に含まれているかのようにグループ化されます。 whenマクロの展開は本質的に次のとおりです。

(if (foo-p foo)
  (progn
    form1
    form2)
  nil)

Prog1およびProg2

多くの場合、複数の式を評価し、最後の式ではなく最初の式または2番目の式から結果を返すと便利です。これはletとを使って簡単に実現できます:

(let ((form1-result form1))
  form2
  form3
  ;; ...
  form-n-1
  form-n
  form1-result)

このフォームは、いくつかの用途では一般的であるので、Common Lispには、 PROG1PROG2 のprognのようであるが、第1及び第2形態の結果を返します。例えば:

(prog1
  42
  (print 'hello)
  (print 'goodbye))
;; => 42
(prog2
  (print 'hello)
  42
  (print 'goodbye))
;; => 42

prog1と prog2prognの重要な違いは、 prognは最後のフォームのすべての値を返しますが、 prog1prog2は第1と第2のフォームのプライマリ値のみを返します。例えば:

(progn
  (print 'hello)
  (values 1 2 3))
;;=> 1, 2, 3

(prog1
  (values 1 2 3)
  (print 'hello))
;;=> 1              ; not 1, 2, 3

prog1形式の評価を持つ複数の値の場合は、代わりにmultiple-value-prog1を使用します。同様のマルチプルバリュープログ2はありませんが、必要に応じて実装するのは難しくありません。

ブロック

特殊演算子ブロックは、(暗黙のprognような)いくつかのLispフォームのグループ化を可能にし、ブロックに名前を付けるための名前も取ります。ブロック内のフォームが評価されると、特殊演算子return-fromを使用してブロックを終了することができます。例えば:

(block foo
  (print 'hello)     ; evaluated
  (return-from foo)
  (print 'goodbye))  ; not evaluated
;;=> NIL

return-fromには戻り値も指定できます。

(block foo
  (print 'hello)     ; evaluated
  (return-from foo 42)
  (print 'goodbye))  ; not evaluated
;;=> 42

名前付きブロックは、コードの塊が意味のある名前を持つ場合、またはブロックがネストされている場合に便利です。状況によっては、ブロックから早期に復帰する能力だけが重要です。その場合、ブロック名としてnilを使用して戻り値を返すことができます。 戻り値は、ブロック名が常にnilであることを除いて、 return-fromと似ています。

注:囲まれたフォームはトップレベルのフォームではありません。それは異なっていprognトップレベルの囲まれたフォーム、 prognフォームがまだトップレベルのフォームを考えられています。

タグボディー

グループフォームでたくさんのコントロールを行うには、 タグボディの特別な演算子が非常に役に立ちます。 タグ ボディフォーム内のフォームは、 goタグ (シンボルまたは整数のみ)または実行するフォームのいずれかです。 tagbodyの中で、 行く特別な作業は、新しい場所に実行を転送するために使用されます。このタイプのプログラミングは、任意の実行パスを可能にするので、かなり低レベルとみなすことができます。以下は、for-loopがtagbodyとして実装されたときのようなものの冗長な例です:

(let (x)               ; for (x = 0; x < 5; x++) { print(hello); }
  (tagbody
     (setq x 0)
   prologue
     (unless (< x 5)
       (go end))
   begin
     (print (list 'hello x))
   epilogue
     (incf x)
     (go prologue)
   end))

tagbodygoは一般的に使用されていませんが、おそらく "GOTOは有害なものとみなされます"が原因ですが、ステートマシンのような複雑な制御構造を実装するときに役立ちます。多くの反復構造体は暗黙のタグボディにも拡張されています。たとえば、 dotimesの本文は一連のタグとフォームとして指定されます。

どの形式を使用するのですか?

グループ化が必要なフォームに展開するマクロを書くときには、どのグループ化構造を拡張するかを考えておくと時間がかかります。

その子フォームがあるので、定義スタイルのフォームは通常、トップレベルのフォームとして表示されます、例えば、 定義ウィジェットマクロ、およびいくつかの関数定義 s もの、defstruct Sなどのために、それは通常、 のprognを使用することは理にかなっていますトップレベルのフォームとして処理されます。繰り返しフォームの場合、暗黙のタグボディがより一般的です。

たとえば、 dotimesdolist 、およびdoの本体は、暗黙のタグボディに展開されます。

コードの名前付き "チャンク"を定義するフォームでは、暗黙的なブロックが便利なことがよくあります。例えば、 defunの本体は暗黙のprognの内部にありますが、暗黙のprognはその関数の名前を共有するブロック内にあります。つまり、 return-fromを使用して関数を終了することができます。そのようなコンプ



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow