TeX Alchemist Online

TeX のこと,フォントのこと,Mac のこと

文字列一致判定を完全展開可能な形で行う

ifthen パッケージによる文字列一致判定

LaTeXマクロで「文字列一致判定」による場合分けを行いたい場合,ifthen パッケージの \equal を使うのが簡単です。ifthen パッケージは次の記事に詳しく解説されています。

qiita.com

例えば,#1に与えられた文字列が ほげ であるか否か(完全一致)によって \TRUE\FALSE のいずれかを実行するマクロ \hogecheck は次のように定義できます。

\documentclass{jlreq}
\usepackage{ifthen}

\def\TRUE{一致しました!☃}
\def\FALSE{一致しません☂}

\newcommand\hogecheck[1]{\ifthenelse{\equal{#1}{ほげ}}{\TRUE}{\FALSE}}

\begin{document}

\hogecheck{ほげ}% => 一致判定
\hogecheck{}% => 不一致判定
\hogecheck{☃☃☃}% => 不一致判定
\hogecheck{ほげほげ}% => 不一致判定
\hogecheck{☃ほげ}% => 不一致判定
\hogecheck{ほげ☃}% => 不一致判定
\hogecheck{☃ほげほげ☃}% => 不一致判定
\hogecheck{☃ほげ☃ほげ☃}% => 不一致判定

\end{document}

問題点

ですが,\ifthenelse を使うと完全展開可能性が要求される文脈下で使えないという問題があります。

% \edef の中身に使えない
\edef\result{\hogecheck{ほげ}} % => コンパイルエラー
% \csname ~ \endcsname による動的な制御綴生成に使えない
\csname\hogecheck{ほげ}\endcsname % => コンパイルエラー
% \input によるファイル名に使えない
\input{\hogecheck{ほげ}.tex} % => コンパイルエラー

\def のパターンマッチによる解決策

こういうときは,\def によるパターンマッチを活用して文字列探索を行うというのが常套手段となります。

\def\TRUE{YES}
\def\FALSE{NO}

\makeatletter
\def\hogecheck#1{%
  \@hogecheck#1ほげ\relax
}

\def\@hogecheck#1ほげ#2\relax{%
  \ifnum0\if"#1"1\fi\if"#2"\else1\fi=11
    \@@hogecheck#1#2\relax
  \else
    \FALSE
  \fi
}

\def\@@hogecheck#1ほげ#2\relax{%
  \ifnum0\if"#1"1\fi\if"#2"1\fi=11
    \TRUE
  \else
    \FALSE
  \fi
}
\makeatother

\hogecheck はダミーの ほげ を含めて引数を \@hogecheck に投げます。次に \@hogecheck は,「#1 が空かつ#2が空でない」という条件の可否を調べることで,元の \hogecheck の引数内に ほげ が含まれているか否かを判定します。そして,それが成立する場合には,ダミーの「ほげ」を除いた部分を \@@hogecheck に投げ,\@@hogecheck は「ほげ」の前後に余計なトークンが含まれていないかどうかを調べることで,引数が「ほげ」と完全一致するか否かを判定する,という仕組みになります。

\if系トークンによる空文字列判定,およびAND条件の表現には,以前の記事で紹介したテクを活用しました。

doratex.hatenablog.jp

\def のパターンマッチによる文字列探索は,去年の記事でも用いました。

doratex.hatenablog.jp

さて,これで完全展開可能な文字列一致判定ができましたので,このように定義した \hogecheck を用いて,次のような処理が可能となります。

\def\TRUE{YES}
\def\FALSE{NO}

\edef\result{\hogecheck{ほげ}}
 % => \result は macro:->YES で定義される
\edef\result{\hogecheck{ふが}}
 % => \result は macro:->NO で定義される

\csname\hogecheck{ほげ}\endcsname % => \YES になる
\csname\hogecheck{ふが}\endcsname % => \NO になる

\input{\hogecheck{ほげ}.tex} % => YES.tex がロードされる
\input{\hogecheck{ふが}.tex} % => NO.tex がロードされる

飛び道具:expl3 による解決策

実は,このような古風でトリッキーな解決策を組み立てなくとも,expl3 が LaTeX カーネルに組み込まれている現代ならば,飛び道具として expl3 の \str_if_eq:nnTF(これは e-TeX の追加プリミティブである \pdfstrcmpを使って実装されています)を使うことで,問題は一気に解決します。これが今風の解決策でありましょう。

\def\TRUE{YES}
\def\FALSE{NO}

\ExplSyntaxOn
\cs_new_eq:NN \CompareStringsTF \str_if_eq:nnTF
\ExplSyntaxOff

\edef\result{\CompareStringsTF{ほげ}{ほげ}{\TRUE}{\FALSE}}
 % => \result は macro:->YES で定義される
\edef\result{\CompareStringsTF{ふが}{ほげ}{\TRUE}{\FALSE}}
 % => \result は macro:->NO で定義される

\csname\CompareStringsTF{ほげ}{ほげ}{\TRUE}{\FALSE}\endcsname
 % => \YES になる
\csname\CompareStringsTF{ふが}{ほげ}{\TRUE}{\FALSE}\endcsname
 % => \NO になる

\input{\CompareStringsTF{ほげ}{ほげ}{\TRUE}{\FALSE}.tex}
 % => YES.tex がロードされる
\input{\CompareStringsTF{ふが}{ほげ}{\TRUE}{\FALSE}.tex}
 % => NO.tex がロードされる