同じ書式で囲んだ領域を文書中で繰り返すなら、自分専用の \begin{名前}…\end{名前} を作れます。基本は \newenvironment。マクロが「命令」の自作なら、環境はその ブロック版 です。開始時・終了時に走るコードの書き分け、引数の渡し方とその落とし穴、既存環境の作り直し、そして現代的な \NewDocumentEnvironment まで、環境を自作する土台をひととおり見ていきます。
\newenvironment で環境を作る
基本の道具が **\newenvironment{name}{⟨begin-def⟩}{⟨end-def⟩} です。第 1 引数に作りたい環境の名前(バックスラッシュは付けません)、続く 2 つの引数に 開始時のコード と 終了時のコード** を書きます。\begin{name} に出会うと ⟨begin-def⟩ が、\end{name} に出会うと ⟨end-def⟩ が実行され、その間に挟まれた本文がふつうに組まれます。
% プリアンブルで定義 / define in the preamble
\newenvironment{warning}{%
\par\noindent\textbf{注意:}\itshape
}{%
\par
}
% 本文で使う / use it in the body
\begin{warning}
この操作は元に戻せません。
\end{warning}決定的に大事な性質として、環境はそれ自体が暗黙のグループ(局所的な範囲)を作ります。LaTeX の規定では「⟨begin-def⟩ のコード・本文・⟨end-def⟩ のコードは、すべて一つのグループの中で処理される」と定められています。したがって ⟨begin-def⟩ の中で行った書体やスペーシングの変更は、\end{name} を抜けると自動的に元へ戻り、外の本文には漏れません。上の例で \itshape(斜体)を明示的に戻していないのに本文だけ斜体になるのは、このためです。
この「自動でグループになる」性質は、書体や余白の変更を一定範囲に閉じ込めたいときに大変便利です。マクロ(\newcommand)で同じことをすると、中身を { … } で自分でくくってグループを作る必要がありますが、環境では \begin…\end がその役目を兼ねます。
引数をとる環境と「終了コードは引数を見られない」
中身を呼び出しごとに変えたいなら、引数を持たせます。\newcommand とまったく同じ書き方で、名前のあとの角括弧に 引数の個数 を書き、⟨begin-def⟩ の中で **#1・#2 … と参照します。\newenvironment{name}[⟨nargs⟩]{⟨begin-def⟩}{⟨end-def⟩}** の形です(#1 から #9 まで、最大 9 個)。
% 見出しの語を引数で受け取る / take the heading word as an argument
\newenvironment{point}[1]{%
\par\noindent\textbf{#1}\quad
}{%
\par
}
\begin{point}{結論}
早めにバックアップを取ること。
\end{point}さらに、\newcommand と同じく 最初の引数を「省略可能」にして既定値を与える こともできます。**\newenvironment{name}[⟨nargs⟩][⟨default⟩]{...}{...}** のように角括弧を 2 つ重ねると、#1 がオプション引数になり、その既定値が ⟨default⟩ になります。呼び出しは \begin{name}(既定値を使う)または \begin{name}[x](#1 を x にする)。[⟨nargs⟩] には オプション引数も含めた総数 を書く点も \newcommand と共通です。
ここで環境特有の、そして最大の落とし穴があります。**引数 #1・#2 … は ⟨begin-def⟩ でしか使えず、⟨end-def⟩ の中では使えません**。規定にも「終了コードはパラメータを含んではならない、つまり #1・#2 などをここで使うことはできない」と明記されています。理由を一言でいえば、\end{name} の時点ではもう引数が手元に残っていないからです。⟨end-def⟩ の中で #1 と書くと、引数が展開されるのではなくエラーになります。
終了時にも引数の値が必要なら、定石は **⟨begin-def⟩ のうちにその値を保存しておく ことです。文字列なら ボックス(\newsavebox で確保し \sbox に詰める)** に、あるいはマクロに \newcommand/\def で覚えさせ、⟨end-def⟩ ではそれを取り出します。環境全体が一つのグループなので、⟨begin-def⟩ で保存した内容は ⟨end-def⟩ まで生き残ります。次は引用の最後に出典を出す例で、出典の語を #1(既定 Shakespeare)で受け取り、ボックス \quoteauthor に保存して終了時に使っています。
\newsavebox{\quoteauthor}
\newenvironment{citequote}[1][Shakespeare]{%
\sbox\quoteauthor{#1}% begin-def で引数を保存 / save the arg here
\begin{quotation}
}{%
\hspace{1em plus 1fill}---\usebox{\quoteauthor}% end-def で取り出す
\end{quotation}%
}
\begin{citequote}
To be, or not to be.
\end{citequote}
\begin{citequote}[Knuth]
Premature optimization is the root of all evil.
\end{citequote}\renewenvironment と「すでにある名前」
\newcommand と同じ安全装置が環境にもあります。**\newenvironment は、その名前がまだ定義されていないときだけ成功** します。すでに存在する環境名に使うと、LaTeX Error: Command \name already defined. というエラーで止まります(環境 name は内部的に \name というコマンドを使うため、このメッセージになります)。これは既存の環境をうっかり上書きする事故を防ぐための、わざとの仕様です。
既存の環境を作り直したいときは **\renewenvironment** を使います。引数の書き方([⟨nargs⟩][⟨default⟩] も含めて)は \newenvironment とまったく同じで、違うのは振る舞いだけ。\renewenvironment は対象が すでに定義されていないとエラー になります(\newenvironment のちょうど裏返しです)。標準の quote や itemize のような既存環境の中身を差し替えるときに使います。
% 既存の quote を、本文を斜体にする版へ作り直す
\renewenvironment{quote}{%
\list{}{\rightmargin\leftmargin}\item\relax\itshape
}{%
\endlist
}いくつか守るべき規則があります。第一に、**環境の名前は文字列 end で始められません**(\end{...} の処理は内部で \end<名前> という名のコマンドを使うため、衝突を避ける制約です)。第二に、\newenvironment と \renewenvironment には名前の末尾に * を付けた スター付きの派生形 があり、これは引数末尾の空白の扱いが異なります(通常はスターなしで十分です)。\providecommand に相当する「無いときだけ定義する」環境版は標準では用意されておらず、必要なら次節の現代的なインタフェースを使います。
よく使う組み立て方
実務で環境を自作する場面は、おおむね次の三つに集約されます。いずれも ⟨begin-def⟩ で「下ごしらえ」をし、⟨end-def⟩ で「後始末」をする、という同じ骨格です。
- 書式で包む …
⟨begin-def⟩で書体・サイズ・寄せを設定し、本文をその書式で組む。グループ性のおかげで⟨end-def⟩は空でも自動的に元へ戻る。 - 前後に空きを入れる …
⟨begin-def⟩の先頭と⟨end-def⟩の末尾に\par\medskipなどの縦アキを置き、本文を上下の余白で囲む。 - 既存の環境の上に作る …
⟨begin-def⟩で別の環境を\begin{...}し、⟨end-def⟩で対応する\end{...}を閉じる。quoteやcenter、listを土台に少しだけ味付けする使い方。
三つ目の「既存の環境の上に作る」が最もよく使われます。たとえば、quotation をそのまま使いつつ小さめの文字で組む smallquote 環境は、⟨begin-def⟩ で \small を効かせてから \begin{quotation} を開き、⟨end-def⟩ で \end{quotation} を閉じるだけです。土台の環境の挙動(字下げや余白)はそのまま受け継がれます。
% 1) 書式で包む / wrap in formatting
\newenvironment{aside}{\par\small\itshape}{\par}
% 2) 前後に空きを入れる / add space around
\newenvironment{spaced}{\par\medskip\noindent}{\par\medskip}
% 3) 既存の環境の上に作る / build on quotation
\newenvironment{smallquote}{%
\small\begin{quotation}
}{%
\end{quotation}
}注意点として、⟨begin-def⟩ で開いた環境は必ず ⟨end-def⟩ で閉じ、対応をそろえてください。また、⟨begin-def⟩ の末尾やコード行の終わりに付く 意図しない空白 が出力に紛れ込まないよう、行末に % を置く習慣も役立ちます(上の複数行の例で各行末に % を付けているのはこのためです)。
現代的な \NewDocumentEnvironment
もう一つ、現代の LaTeX が用意する強力な定義法が **\NewDocumentEnvironment{name}{⟨arg-spec⟩}{⟨begin-code⟩}{⟨end-code⟩}** です。マクロ側の \NewDocumentCommand と対をなすもので、もとは xparse パッケージの機能でしたが、2020 年 10 月以降は LaTeX カーネルに取り込まれ、\usepackage なしで使えます。引数の個数を数字で書く代わりに、m(必須)・o(省略可能)・O{既定値}(既定値つき)・s(星の有無)といった 「引数指定(arg-spec)」 の文字列で宣言します(指定子の詳細は「xparse」のページへ)。
このインタフェースが古い \newenvironment より優れている点は二つあります。一つは、オプション引数を複数とったり、星付き(starred)の変種を正式に扱える こと。もう一つが決定的で、**引数を ⟨begin-code⟩ だけでなく ⟨end-code⟩ でも使える** ことです。規定にも「⟨begin-code⟩ と ⟨end-code⟩ のどちらも、⟨arg-spec⟩ で定義した引数にアクセスできる」と明記されています。さきほどの「保存ボックス」の手間が不要になる、というわけです。
% #1 を begin でも end でも使える / #1 usable in both begin and end
\NewDocumentEnvironment{citequote}{O{Shakespeare}}{%
\begin{quotation}
}{%
\hspace{1em plus 1fill}---#1%
\end{quotation}%
}
\begin{citequote}[Knuth]
Premature optimization is the root of all evil.
\end{citequote}\NewDocumentEnvironment にも、\newenvironment の仲間に対応する **\RenewDocumentEnvironment(再定義)・\ProvideDocumentEnvironment(無いときだけ定義)・\DeclareDocumentEnvironment(既存を問わず定義)** がそろっています。新しく書くコードでは、引数まわりの自由度と「終了コードでも引数が使える」利点から、こちらの現代的なインタフェースを使うのがおすすめです。