マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

短縮命令生成器生成器を作る件について

掲示板のアレの話。

  • \generateabbrevdef{XXX}{\CS}{<本体>}(ただし一般的に <本体> は第 2 引数の制御綴 \CS を含む)は以下の仕様の命令 \defineabbrevXXX を定義する。
    • \defineabbrevXXX{<文字列>,...} は、引数のリストの中の各々の文字列 Y について、以下の命令 \XXXY を定義する。
      • \XXXY は、<本体> の中の \CSY に置き換えたトークン列を実行(出力)する。

本当にこんな複雑なものが必要なのかは、小一時間問い詰める価値がありそうだが、それはともかく、元ネタの失敗例のコードに近い形で実装すると次のようになる。

\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 の定義は「#1Y に置換した列」とするのである。これだと、必要に応じて展開可能な短縮形を生成することも可能となる。((前の方法だと、\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 のあるトークン列がきちんとマクロ定義の本体の位置に配置されることになる。