最近書いた (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 までの太字コマンド量産するスクリプト書くほうが早かった説はある
- まあ定義したいものの複雑さに応じてケースバイケースみたいな感じですよね
- これを真面目に説明しているせいでゼミのスライド作りが終わらない
- みんなの闇マクロ定義も教えてください