最近書いた (La)TeX の闇コード
これは ████████ Advent Calender 3 日目の記事です.
秘伝のコードが動かない
弊ラボに伝わるコードの一部です:
\newcommand{\vA}{{\bm{A}}}
\newcommand{\vB}{{\bm{B}}}
\newcommand{\vC}{{\bm{C}}}\newcommand{\X}{{\bm{X}}}
\newcommand{\Y}{{\bm{Y}}}
\newcommand{\Z}{{\bm{Z}}}\bm だけでなく \mathcal とか \mathbb に対しても
同じような定義があって,そういあうたりにもやもやしながらも放置してたんですが,
すでにわらわらパッケージを読み込んでいる Beamer ベースのファイルに
このファイルを食わせたところ悲劇が起こり,
具体的には\x あたりが働かなくなってしまった.
どうせ全部の命令の頭に v つけるんなら \foreach あたりを使って
なんかうまい具合にできるんじゃないか,
と思っていろいろやった結果いい感じになった話です.
こうなった
\usepackage{pgffor}
\usepackage{bm}
\usepackage{amsmath,amssymb}
% Upper-case alphabet
\foreach \char in {A,...,Z}{
% bold
\expandafter\xdef\csname v\char\endcsname{%
\noexpand\bm{\char}%
}
% cal
\expandafter\xdef\csname c\char\endcsname{%
\noexpand\mathcal{\char}%
}
% mathbb
\expandafter\xdef\csname b\char\endcsname{%
\noexpand\mathbb{\char}%
}
}これは何をしているのか
僕は TeX レベルの命令とかにあまり詳しくないので適当を言ってる可能性があります. 誤りの指摘などは歓迎します.
pgffor パッケージと \foreach に文字を渡す用法については
PGF Manual の
“83 Repeating Things: The Foreach Statement” (901 ページから)を読みましょう.
とりあえずこう書くと \char が A から Z と順々に定義されるんだ,
と思ってくれればいいです.
ループのなかでは \vA とか \cA とか \bA みたいな命令を定義しています.
3 つの形は一緒なので,
% bold
\expandafter\xdef\csname v\char\endcsname{%
\noexpand\bm{\char}%
}\char は A と定義されてることにします.
先に 1 行目から考えます.
\expandafter\xdef\csname v\char\endcsname{%\csname / \endcsname から説明してみます.
\csname は対になる \endcsname までのトークン列を解釈して,
解釈した結果の文字列を名前とする命令に『展開』されます.
『展開』というのはまあ C とかでいうマクロ展開みたいな意味合いでの
『展開』だと思えばいいと思います.
\csname foo\endcsname\foo\csname は \endcsname までに含まれるマクロを展開するので,今回の例
\csname v\char\endcsname\vA\xdef はマクロを定義する命令 \def の仲間で,
具体的には \global\edef と等価です.
まず \def は
\def\foo{ABC}\def \macroname {replacement} の形で使います.
次に \edef は {replacement} の中身に命令を展開した結果を置きたいときに
使います.今回話題にしている \char を使って説明すると,
\def\bar\char\bar は以降それが置かれた場所で \char として解釈されることになりますが,
\edef\bar\char\bar は A と解釈されることになります.
\def / \edef の前に \global を置くと
定義した命令は文書全体で有効になります.
\foreach ループの中にローカルなスコープがあるため,
その外でも利用したい命令を定義するには
\xdef (= \global\edef) を使う必要があります.
\expandafter はここまで説明したものに比べるとちょっとトリッキーな命令
(と僕は思う)ですが,ここまで挫折せずに読めてれば理解できると思います.
挫折してしまうのは僕の文章力が足りないせいかもしれないのにこんなこと言うのは自分の文章力に絶対の自信がありそうでヤバいなって思いました.
\expandafter は展開の順番を変える命令で,
『ひとつ後のマクロを展開する前に』ふたつ後のマクロを展開します.
\expandafter\xdef\csname v\char\endcsname{%
\noexpand\bm{\char}%
} \xdef\vA{% \xdef\csname v\char\endcsname{%\csname の定義が置換されてすべてが破滅します.
\expandafter\xdef\csname v\char\endcsname{%\expandafter が前に置かれることで
\csname ~ \endcsname が先に解釈されて,
\xdef\vA{%次に \xdef の中身,
\noexpand\bm{\char}%\noexpand は edef のなかだけど次にくるマクロの展開はしたくない,
というときに使います.ここでなぜ \bm や \mathcal や \mathbb の展開を
抑止しなくてはならないのかはよくわからないんですが,
実際 \noexpand 外すとエラーで怒られるし,そういうもんなんだと思ってます.
(知ってたら教えてほしい……僕が理解できるかみたいなところもあるけれど)
\edef によって \char は展開されるので,結果として
\expandafter\xdef\csname v\char\endcsname{%
\noexpand\bm{\char}%
} \xdef\vA{%
\bm{A}%
}まとめ
\foreachの便利(?)用法『文字をループ変数で扱える』の紹介- こんなメタな使い方でなくても
$a_1$, $b_1$,みたいなのを 大量生産するみたいな用途にも便利だと思います - TikZ と組み合わせて各座標にノード置くとかもできる
- こんなメタな使い方でなくても
\expandafter/\csnameなど LaTeX レベルだと触れないコマンドのさわりの紹介- スタイルファイルとかの TeX レベルのファイルにはよく出てきます
- TeX 内で閉じるという気持ちよさはあるけど,適当な言語で
A から Z までの太字コマンド量産するスクリプト書くほうが早かった説はある
- まあ定義したいものの複雑さに応じてケースバイケースみたいな感じですよね
- これを真面目に説明しているせいでゼミのスライド作りが終わらない
- みんなの闇マクロ定義も教えてください