* 基礎知識 [#s5fc1cf9]

TeX の特徴の一つにマクロが作成できることが挙げられます。
マクロを活用することによって、ミスの少ない仕上がりや作業効率の向上などの効果が期待できます。
また、実用的な面を持つだけではなく、知的な趣味としても愉しむことができます。

マクロの作成技術を身につけるにあたって必要な基本知識を列挙しておきましょう。
これらの事柄を調べてゆくと自ずとマクロ作成技術が身に付くことでしょう。

参考書について:マクロを作成する上で必要となる TeX のプリミティブ命令の多くは、LaTeX の入門レベルの解説書では触れられていないことが多いです。『TeXブック』には当然解説されていますが、入手は困難なようです。

- トークン (文字トークンとコントロールシークエンス(コントロールワード/コントロールシンボル))
- カテゴリーコード (\catcode, \makeatletter, \sanitize)
- 基本レジスタ (\count, \dimen, \skip/\muskip, \box, \toks, 数値と数字の違い)
- 定義と代入 (\def, \let, \newcommand, \DeclareRobustCommand)
- グルーピング ({, }, \bgroup, \egroup, \begingroup, \endgroup)
- 条件分岐 (\if, \ifx, などの各種 \if)
- 展開の制御 (\expandafter, \edef, \noexpand, \afterassignment)
// \aftergroup は有効な使用例が少ないため,保留(ただし,color パッケージで
// きわめて効果的に用いられています).
- トークンの制御 (\the, \string, \csname, 空白トークン)
- ファイル入出力(\openin/\closein, \read, \openout/\closeout, \write,
\immediate)
- デバッグ (\show, \showthe, \meaning, プリミティブとマクロ)
- 組版上の問題に由来する処理(水平モード/垂直モード, \par, \halign/\valign, \mark などの,
一般的なプログラミングの話では出てこない処理を扱うプリミティブ)
// これは,“基本知識”として挙げるべきかどうかは微妙.
// また,1 項目にしてしまうのは乱暴かもしれません.
- LaTeX における慣用的な処理(\@elt を用いたリストの処理や,
\@testopt 等の常用される内部処理用マクロの用法)
- (あと、どうしましょう?)

* 基本練習問題 [#l9a8a164]
// いかがでしょうか。 学習効果が高い、しゃれた問題を作るのは大変そう。
// 『TeXbook』を読めばいいってのは言いっこなし(^^;)
いい問題ができたら書き込みましょう。
// あと,“実際に処理させてみればわかる”という問題以外は“解答”も
// コメントとして入れておいてはどうでしょう.

// 以下の問題では,特に断らない限り,
// - 個々の文字のカテゴリーコードは plain TeX におけるデフォルトの値である
// - 個々の文字の \lccode/\uccode の値は plain TeX におけるデフォルトの値である
// 
// ものと仮定します.
// // 一応,上記の断り書きは入れておいたほうがよろしいのでは?

////// ごもっともです。
////// 入門者向けということで、デフォルトのカテゴリーコードになっていない可能性が
////// あるなどとはつゆほども思わない読者を想定していました。断り書きがなければ
////// 欠陥問題じゃないかと思うような読者にはぜひ執筆側に回ってもらいましょう(^^)

問題によっては解答がコメントとして書かれており,
上の「差分」から見ることができます。

** カテゴリーコード [#gfea22ef]
+ “i”のカテゴリーコードが 0 で、“ ”(空白文字)のカテゴリーコードが 11 である場合、\macro This is a test. はどのようにトークン化されるか?
// \relax の代わりに [relax] と書いて,個々のコントロール・ワードを
// 明示することにすると,この問題の \macro This is a test. は
// [macro Th][s ][s a test].
// (最後のピリオドのみ,文字トークン)のように扱われる(ただし,問題文中で
// 断ってある i と空白文字以外の文字については,カテゴリーコードの変更は
// なされていないものと仮定).
+ ファイル latex.ltx における \strip@pt の定義を解析し,
このマクロの内部処理で用いられる \rem@pt の定義を単に
\def\rem@pt#1.#2pt{#1\ifnum#2>\z@ .#2\fi} としたのでは
うまくいかない理由を述べよ.
// \rem@pt では“\the + \dimen レジスタ”が与える文字列の末尾の“p_{12}t_{12}”
// を取り除くことになるので,通常の文字列“p_{11}t_{11}”を書式指定文字列に
// したのではパターンマッチが意図通りには行われない.

** 定義と代入 [#gcea75b2]
+ \def\macro#1#2a#3.{#1;#2;#3;} と定義したとき、 \macro This is a test. の展開結果はどうなるか? \def\macro#1a#2#3.{#1;#2;#3;} だとしたらどうか?
+ あるユーザが,次のような記述を行った(\@firstoftwo,\@secondoftwo
については,必要があればファイル latex.ltx を参照のこと).
 \documentclass{article}
 \begin{document}
 \makeatletter
 \def\issubstring#1#2{%
    \def\@tempa##1#1##2\@nil{\def\@tempa{##2}}%
    \@tempa#2#1\@nil
    \ifx\@tempa\@empty \expandafter\@secondoftwo
    \else              \expandafter\@firstoftwo
    \fi}
 \makeatother
 \def\test#1#2{\issubstring{#1}{#2}%
    {``#1'' is contained in ``#2''.}%
    {``#1'' is not contained in ``#2''.}}
 \test{cat}{dog}
 \end{document}
// つまり,
// \issubstring は第 1 引数が第 2 引数の部分文字列であれば第 3 引数を実行し,
// そうでなければ第 4 引数を実行するようなマクロというつもりである.
実際,上記の \test{cat}{dog} の出力は意図通りのものであった.
問: \issubstring はどのようなマクロで,
\test{cat}{dog} の出力はどうなっていたのか?~
しかし,\test{aa}{a} を処理させるととんでもない結果が得られ,
このユーザは頭を抱えた.
// 実際,“aa”が“a”の部分文字列と判定されてしまったのだが,
問: いったいどのような結果が得られたのか?
また,\issubstring の定義のどこに問題があり,どう修正すべきであったか?
// \@tempa#2#1\@nil の行が問題.
// 実際,\issubstring の第 1 引数が ABA の形で,第 2 引数は AB の形である
// 場合(e.g. \issubstring{cat-cat}{cat-} などの場合)には,
// 第 2 引数と第 1 引数の先頭部分がつながって“意図しない位置に”\@tempa の
// 書式指定文字列が生じてしまう.
// ここでは,\@tempa#2\@empty#1\@nil のように \@empty を入れればよい.
// なお,\@empty も \issubstring の引数として用いられかねないような状況では,
// \@empty の代わりに %_{12} あるいは ^^@_{12} あたりを用いればよいだろう.
// [余談] LaTeX の内部処理用マクロ \in@ にもまったく同じ問題があるので,
// ユーザ自身が \in@ を用いる場合には注意が必要.
+latex.ltx内で定義されているマクロ\@defaultunitsの使い方とその効果を述べ,定義を解析せよ.
// 文法上は \@defaultunits <たいていの代入操作><ごみ(存在しなくてもいい)>\@nnil
// という形式で用いることができるが,使用例は
//   \def\setparindent#1{\@defaultunits\parindent#1pt\relax\@nnil}
// のような具合に
//   \@defaultunits<寸法を代入できるもの><寸法または実数><単位>\relax\@nnil
// となっていることが大半(というより,そのような用法を念頭に置いていると
// 思われる).ここで,\relax は“代入に伴って,単位の後に続くマクロが
// 不用意に展開される”ことを防ぐために必要であることに注意.
// なお,今例示した \setparindent は引数として寸法または実数値
//(を 10 進表記した文字列)をとり,引数が寸法であるときには
// \parindent をその引数に設定し,数値であるときには単位 pt を補ってできる
// 寸法を \parident に代入するマクロ.\@defaultunits の効果(として
// 念頭に置いていると思われるもの)はこの例のように“必要に応じて
// デフォルトの単位を補うような”寸法の代入ができる,ということである.
// 問: \@defaultunits の文法上の書式の中で <たいていの代入操作> と
// 書いているように,\@defaultunits の直後では使えないような代入操作も
// 存在する.そのような操作の例を挙げよ(cf. “The TeXbook”の第 24 章に
// おける \afterassignment の説明).
+ 2 個のマクロ \macroA,\macroB の定義を入れ換える際に,
 \def\temp{\macroA}
 \def\macroA{\macroB}
 \def\macroB{\temp}
という記述と
 \let\temp\macroA
 \let\macroA\macroB
 \let\macroB\temp
という記述のどちらが意図通りに処理されるか?
また,意図通りに処理されないほうはどのような結果になるか.
// // \def と \let の相違を確認させる問題で,もっと気の利いた問題が
// // ありましたら,ぜひこの問題と差し替えてください(出題者より).
// 意図通りに処理されるのは後者.
// 前者では \macroA,\macroB のどちらを用いても無限ループに陥る(か,
// \macroA,\macroB を用いるまでに \temp が再定義されていたら
// “無関係なもの”が実行されてしまう).
// 実際,\temp が再定義されなかった場合について展開過程を
// 考えてみると,\macroA → \macroB → \temp → \macroA(以下,
// 繰り返しになる)のようになっている.

**展開の制御 [#h72dcb23]
+ \def\A#1,#2{#2#1} \def\B#1;#2{#1,#2} \def\C{x;y} と定義されているとき、\A\B\C に\expandafter を適宜挿入して、展開結果が yx となるようにせよ。 
// \expandafter\expandafter\expandafter\A\expandafter\B\C

**デバッグ [#me44b82b]
+ あるユーザが,一時的に文字列の幅を測定してその結果を用いるつもりで
 \def\mymacro#1{%
   \newdimen\tempwidth
   \settowidth\tempwidth{#1}%
   ...}
のような定義を行ったところ,別のユーザから“本当にそう定義していいかい?
そのマクロを 256 回以上使ってみるとどうなる?”とツッコミが入った.
上記の定義の問題点および対処法を述べよ.
// 問題点: 一時使用用のレジスタを \mymacro を用いるたびに新規割り当て
// しているため,いずれレジスタを使い果たしてしまう(eTeX ベースの
// システムではこの問題はあまり顕在化しないとは思うが,問題であることには
// 変わりがない).つまり,“メモリリーク”が存在する.
// 解決法: 一時使用用のレジスタはマクロの置換テキストの外部で割り当てればいい.
// e.g.
// \newdimen\tempwidth
// \def\mymacro#1{\settowidth\tempwidth{#1}...}
// なお,“ローカルに使う変数”が要る場合には,レジスタをローカルに
// 割り当てようとするのではなく(実際,\newdimen などのレジスタ
// 割り当て用マクロではグローバルにしか割り当てない),
// “レジスタの値のスコープ”をグルーピングで制御すればよい.

** 組版上の問題に由来する処理 [#o0e908b4]
+次のように書いたときに「あ」だけの行ができるのはなぜか?&br;
また,どうすれば「あれ,なんで?」と一行にできるか?&br;
 \par
 \hbox{あ}
 れ,なんで?
// \par によって垂直モードに移行し,次の \hbox{あ} はそのまま垂直モードで
// 処理されていることによる.ここでは,\leavevmode を \hbox{あ} の
// 直前に置いて明示的に水平モードに移行すれば,“あ”のみの行ができることは
// 回避される(もっとも,“\hbox{あ}れ,…”ではなく単に“あれ,…”
// としてもこの問題の文字列に関しては解決するが,これは出題意図から
// 外れているだろう).
// 確かに意図とは違いますが,正当な理解に基づいての\hboxを外すという解は
// 正解ですね.ですので最初に「なぜか」という問いかけがあります
+あるユーザが今何ページであるかを本文中に書くために\thepageを記述した.ところが,正しくノンブルを拾えているところと,そうではないところが発生してしまった.これはなぜか(正しく拾えているところとそうではないところに何らかの傾向があるはずである).
// TeX の組版処理が“段落指向”であることによる.
// 例えば,ある段落が複数のページにまたがっている場合,
// その段落の中のページ分割位置以降の部分に \thepage が書かれていると,
// その \thepage に対応する出力は段落の開始位置を含むページ(状況によっては
// それ以前のページとなることもありうる)のページ番号となってしまう.
// 実際,“段落の組版処理を済ませて個々の行を作成してから”
// ページ分割位置が割り出されるため,段落の組版処理(その際に,段落全体が
// 文字列レベルでは確定する)の際にはページ分割に伴う \thepage の更新など
// 行わないで処理を済ませてしまう.

-----
- 「入門」の一項目にするのではなく,別途「マクロの作成・応用」のようなページにされた方が良いように思いますが,いかがでしょうか。 この内容は「入門」のレベルを逸脱しているように思いましたので -- トニイ 2006/06/16

- [北見 けん] まあ、もう少しコンテンツが充実したら構成を見直すということでよろしいかと思います。 まずプリミティブを把握しておけば LaTeX などのマクロは自分で読めるようになるという意味では、入門としてはプリミティブの範囲に収まるものとしてもよいような気もします。 -- 北見 けん 2006/06/16 10:43

- このページの構成自体についてもいろいろご意見があるようなので、コメント欄を追加してみました。ついでに勝手ながら過去のコメントを表に出してみました。-- 北見 けん 2006/06/23
- とりあえず「タネ」を充実させてからでどうでしょう.何問か書きましたが,一日も経たずに解答がついてますね. -- 本田 &new{2006-06-24 (土) 10:53:29};

#comment