* 基礎知識 [#s5fc1cf9]

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. はどのようにトークン化されるか?
+ ファイル 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@ を用いる場合には注意が必要.

**展開の制御 [#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;
#br
\par&br;
\hbox{あ}&br;
れ,なんで?
// \par によって垂直モードに移行し,次の \hbox{あ} はそのまま垂直モードで
// 処理されていることによる.ここでは,\leavevmode を \hbox{あ} の
// 直前に置いて明示的に水平モードに移行すれば,“あ”のみの行ができることは
// 回避される(もっとも,“\hbox{あ}れ,…”ではなく単に“あれ,…”
// としてもこの問題の文字列に関しては解決するが,これは出題意図から
// 外れているだろう).
// 確かに意図とは違いますが,正当な理解に基づいての\hboxを外すという解は
// 正解ですね.ですので最初に「なぜか」という問いかけがあります

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

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