某掲示板のアレの話。
\generateabbrevdef{XXX}{\CS}{<本体>}
(ただし一般的に<本体>
は第 2 引数の制御綴\CS
を含む)は以下の仕様の命令\defineabbrevXXX
を定義する。\defineabbrevXXX{<文字列>,...}
は、引数のリストの中の各々の文字列Y
について、以下の命令\XXXY
を定義する。\XXXY
は、<本体>
の中の\CS
をY
に置き換えたトークン列を実行(出力)する。
本当にこんな複雑なものが必要なのかは、小一時間問い詰める価値がありそうだが、それはともかく、元ネタの失敗例のコードに近い形で実装すると次のようになる。
\documentclass{article} \usepackage{amsfonts} \makeatletter %%%%%%%%%%%%%%%%%%%%%%%%%% \def\generateabbrevdef#1#2#3{% \expandafter\def\csname tmpdefineabbrev#1\endcsname##1{% \expandafter\def\csname #1##1\endcsname{\def#2{##1}#3}}% \expandafter\def\csname defineabbrev#1\endcsname##1{% \expandafter\let\expandafter\@defabbr\csname tmpdefineabbrev#1\endcsname \@for\@tempa:=##1\do{\expandafter\@defabbr\expandafter{\@tempa}}}} \makeatother %%%%%%%%%%%%%%%%%%%%%%%%%% % 生成器を生成する \generateabbrevdef{foo}{\tmpA}{\mathsf{\tmpA}} \generateabbrevdef{bar}{\tmpA}{\mathbb{\tmpA}} \begin{document} % 短縮命令を生成する \defineabbrevfoo{A,B,C} \defineabbrevbar{P,Q,R} \defineabbrevfoo{X,Y,Z} % 使ってみる $\fooA,\barQ,\fooZ$ \end{document}
上の例の場合、命令 \fooA
の定義は以下のものと同等になる。
\def\fooA{\def\tmpA{A}\mathsf{\tmpA}}
\tmpA
の再定義をここに入れるのがポイントである。\fooA
の定義を \edef
で行うことで定義時に \tmpA
を所望の文字列に置き換えるという手段を採ると、\mathsf
まで展開されてしまうので上手くいかない。((補足:「そこは \protect
で何とかする」というのであれば、\edef
でなく \protected@edef
を使えばよい。))
(さてここからが本題。)
上のように「置き換え部分を変数にする」というのは、扱い難いだけでなく、命令設計としてもあまり綺麗でない。その代わりとなる少し面白い方法として、「マクロパラメタを使う」というものを紹介しておく。今の事例の場合、\generateabbrevdef
の書式を以下のようにする。
\generateabbrevdef{XXX}{<#1を含む本体>}
そして、\XXXY
の定義は「#1
を Y
に置換した列」とするのである。これだと、必要に応じて展開可能な短縮形を生成することも可能となる。((前の方法だと、\XXXY
の定義中に必然的に \def
が入るので不可能である。))
\documentclass{article} \usepackage{amsfonts} \makeatletter %%%%%%%%%%%%%%%%%%%%%%%%%% \def\generateabbrevdef#1{% \expandafter\xx@genabbrdef@a\csname xx@defabbr@#1\endcsname{#1}} \def\xx@genabbrdef@a#1#2{% \@namedef{defineabbrev#2}##1{\@for\xx@x:=##1\do{% \expandafter\xx@genabbrdef@b\xx@x\xx@end#1{#2}}}% \def#1##1} \def\xx@genabbrdef@b#1\xx@end#2#3{% \expandafter\xx@genabbrdef@c#2{#1}\xx@end{#3#1}} \def\xx@genabbrdef@c#1\xx@end#2{\@namedef{#2}{#1}} \makeatother %%%%%%%%%%%%%%%%%%%%%%%%%% % 生成器を生成する \generateabbrevdef{foo}{\mathsf{#1}} \generateabbrevdef{bar}{\mathbb{#1}} \begin{document} % 短縮命令を生成する \defineabbrevfoo{A,B,C} \defineabbrevbar{P,Q,R} \defineabbrevfoo{X,Y,Z} % 使ってみる $\fooA,\barQ,\fooZ$ \end{document}
少々高度なコーディングを要する。ポイントとなるのが、\xx@genabbrdef@a
の末尾の \def#1##1
である。これがあるため、\generateabbrevdef
の実行が以下のようになる。((マクロ本体中に ##1
があると、それは #1
に展開されるのであった。))
\generateabbrevdef{foo}{\mathsf{#1}} %(下線部が展開される) ↓ (幾つかの命令が「実行」された後) \def\xx@defabbr@foo#1{\mathsf{#1}}
これで、#1
のあるトークン列がきちんとマクロ定義の本体の位置に配置されることになる。