マクロツイーター

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

xparse で verbatim な引数を扱う話

ステマ
2014/12/01 〜 2014/12/25

TeX & LaTeX Advent Calendar

〜このパッケージがスゴイ!〜
TeX & LaTeX アドベントカレンダー 2014
*  *  *

「とある LaTeX と初心者の物語」の内容を踏まえて、LaTeX の初心者の絶望を防ぐための方策を論じる。具体的には、chemナントカ\smilesobabel を真面目に実装する簡単な方法を述べる。

これが問題だ

まずは問題を整理してみよう。

LaTeX パッケージにおいて、以下のような命令を実装したい。

  • \smilesobabel[<画像読込オプション>>]{<SMILES表現>}{<obabelオプション>} : SMILES表現で与えられた分子の構造式を描画した画像をその場に挿入する。
    • (化学に無縁な人のための説明)
      “SMILES表現”とは、ある種の図式を ASCII 文字列で表現したものである。この文字列を与えて“実際の図式”の PDF 画像ファイルに変換するコマンドが obabel である*1
    • つまり、この命令の実装内部では、シェル実行機能(\write18)を利用して、obabel コマンドを実行し、得られた PDF 画像を graphicx パッケージの \includegraphics 命令で読み込んでいる。
    • 引数の <SMILES表現> はそのまま obabel の引数に渡されている。
    • <画像読込オプション>>\includegraphics のオプション、<obabelオプション> は obabel 起動時に付加されるコマンドオプション。
    • ここで問題なのが、SMILES 表現は〈%〉や〈\〉といった“LaTeX特殊文字”を含みうるということ。これらの文字を含めて、引数に与えた文字列を“そのまま”obabel の引数に渡す必要がある。

ところで、現在ちょうど「TeX & LaTeX アドベントカレンダー」が行われている。その中の 1 日目の記事「xparse パッケージでスゴイ LaTeX マクロを作ろう!」を見てみよう。

なお、引数仕様文字には、ここで紹介しなかったもっと変態なもの(verbatim 引数、区切り付引数、……)も存在するので、変態に興味がある人は xparse のマニュアルを参照してください。

そう、今の問題で必要なのは、まさに「verbatim 引数」という“変態なもの”である。この記事の筆者が敢えて“変態なもの”を説明しなかったのは、恐らくは、「LaTeX 屋は変態に手を出すべきでない」という考えがあったからだろう。しかし今はパッケージの実装(TeX プログラミング)をしている話なので、変態をためらう理由はどこにもない。

verbatim 引数を使う

例の記事を読んでいることを前提とする。

xparse のユーザ命令定義用命令(\NewDocumentComamnd 等)の引数仕様において、文字 v を指定すると、その引数が“verbatim に解釈される”ようになる。すなわち、ASCII 文字で普段のカテゴリコードが 11 と 12 以外のものが、カテゴリコード 12 として読み取られる。*2

% 与えられた引数の文字列を"そのまま"端末に表示する.
\NewDocumentCommand\myTest{v}{%
  \message{arg:<#1>}%
}

verbatim 引数の書き方は 2 通りある。一つは、普通の引数と同じように { } に入れる方法で、この場合は、引数の文字列が { } に関して均衡である必要がある。もちろん引数の中の { } も“そのまま”読み取られる。

\myTest{#$%&{\}^}
  %==>「arg:<#$%&{\}^>」と表示.

もう一つは、\verb の引数と同じように「同じ文字((% \ # { } ^ および空白は使えない。))で囲む」という方法で、この場合は、囲みに使った文字以外の任意の文字の並びを引数に取れる。

\myTest|}_  \expandfater  _{|
  %==>「arg:<}_  \expandfater  _{>」と表示.
実際に使ってみよう

では、実際に xparse を使って、\smilesobabel を「LaTeX特殊文字に対応させる」改修をしてみよう。現状の \smilesobabel の定義は以下のようになっている。((実際のプログラムでは、一旦 \@smilesobabel\newcommand で定義して、その後にそのマクロを \smilesobabel\let でコピーする形をとっている。このようにしている理由はよく解らなかった。ここでは単純化して直接 \smilesobabel を定義する形に直した。))

%% \smilesobabel[<画像読込オプション>]{<SMILES表現>}{<obabelオプション>}
\newcommand\smilesobabel[3][scale=1]{%
  %...定義本体
}

修正は簡単で、単に \newcommand とそのオプションを \NewDocumentCommand に置き換えればよい。引数(#1#2#3)が指しているものは変わらないので、定義本体のコードは全く変える必要がない。

\RequirePackage{xparse}
%% \smilesobabel[<画像読込オプション>]{<SMILES表現>}{<obabelオプション>}
\NewDocumentCommand\smilesobabel{O{scale=1} v m}{%
  %...定義本体
}

ところで、これは個人的な好みの問題であるが、私は <obabelオプション> もオプション引数にして、次のような書式にした方がいいと思っている。*3

%% \smilesobabel[<画像読込オプション>][<obabelオプション>]{<SMILES表現>}
\NewDocumentCommand\smilesobabel{O{scale=1} O{} v}{%
  %...定義本体では #2 と #3 を入れ替える
}
早速ケクレンを描いて晩メシを

以上のように修正した chemナントカ パッケージを使って、早速をケクレンしてみよう。

\documentclass[a4paper]{article}
\usepackage{chemobabel}
\begin{document}
\smilesobabel[width=5cm]{c1cc2cc3ccc4cc5ccc6cc7ccc8cc9ccc%10cc%11ccc%12cc1c1c2cc3c4cc5c6cc7c8cc9c%10cc%11c%12c1}
\end{document}

できた! ……えっ、一部で形が崩れてる? まあ、SMILES は基本的に原子の結合の具合だけ記述していて、原子の位置は“自動で決定している”ので、結果が必ずしも人間が見て「一番綺麗な構造式」にはならないのだろう。

そういうわけで、綺麗な構造式を描きたければ、やっぱり XyMTeX で頑張ろう! (ええっ、まだ別の方法はあるでしょ、これとか)

\documentclass[a4paper]{article}
\usepackage{xymtexpdf}
\begin{document}
\def\?#1#2#{\xHx{#1}{#2}}\def\xHx#1#2#3{\sixfusev[#2{#3}]{}{}{#1}}
\begin{picture}(2500,2300)\put(330,-220){\decaheterov[acfk%
{B\?eac{B\?eac{A\?dbf{A\?dbf{A\?dbf{F\?cae{F\?cae{F\?cae{a{}}}}}}}}}}%
{H\?cbd{F\?cbd{F\?cce{A\?dce{A\?dce{A\?ddf{B\?edf{B\?edf{a{}}[b]}}}}}}}}%
]{}{}}\end{picture}
\end{document}

しまった!! これはケクレンではないっ!!*4

*1:実際には、obabel(OpenBabel)は SVG 画像を出力して、それを Inkscape で PDF に変換しているのだが、ここでは簡略化した設定で考える。

*2:制御文字、および符号値が 0x80 以上の文字のカテゴリコードは変わらない。

*3:一般的に、オプション引数が最後に来ることは避けた方がよい。

*4:この構造式をもつ物質の存在は確認されていない。