algorithm 環境とは別にそれと似た環境を作る
この記事では擬似コードを挿入する LaTeX の環境である algorithm 環境と同等の環境を簡単に作成できるようにする方法を紹介します。このとき algorithm 環境を残したまま新たな環境,例えば protocol 環境を自作することができるようにします。これにより,「アルゴリズム」と「プロトコル」という2つのラベルをもつ環境を下図のように併存させることができます*1。
なお,本記事では jsclasses 系のドキュメントクラス(jsarticle,jsreport,jsbook)の使用を想定して説明を行います*2。
また,この記事は私の LaTeX 擬似コード系ブログの第3弾です。第2弾はこんな記事でした*3。
完成コード
本記事では以下に示す「\newalgfloat」というコマンドの定義方法を説明します。名称 | \newalgfloat |
---|---|
入力 | #1: type,#2: label,#3: ext |
仕様 | ① type という名称の algorithm 環境と同様の環境を作成 ② キャプションに label を指定 ③ type 環境の目次は拡張子 ext のファイルで管理 ④ 環境の目次は jsclasses 系の出力に合わせる ⑤ cleveref に対応させる |
まず完成品を示すと,こんな感じです↓
\usepackage{xparse}% % \usepackage[chapter]{algorithm}% \usepackage[noend]{algpseudocode}% % % define a new algorithm-like environment % (partly cited from algorithm.sty) \def\newalgfloat#1#2#3{% #1 type, #2 label, #3 .ext \floatstyle{\ALG@floatstyle}% % \ifthenelse{\boolean{ALG@within}}{% \ifthenelse{\equal{\ALG@within}{part}}% {\newfloat{#1}{htbp}{#3}[part]}{}% \ifthenelse{\equal{\ALG@within}{chapter}}% {\newfloat{#1}{htbp}{#3}[chapter]}{}% \ifthenelse{\equal{\ALG@within}{section}}% {\newfloat{#1}{htbp}{#3}[section]}{}% \ifthenelse{\equal{\ALG@within}{subsection}}% {\newfloat{#1}{htbp}{#3}[subsection]}{}% \ifthenelse{\equal{\ALG@within}{subsubsection}}% {\newfloat{#1}{htbp}{#3}[subsubsection]}{}% \ifthenelse{\equal{\ALG@within}{nothing}}% {\newfloat{#1}{htbp}{#3}}{}% }{% \newfloat{#1}{htbp}{#3}% }% % \floatname{#1}{#2}% % % for listof"float"s \expandafter\def\csname list#1name\endcsname{% \if@english List of \MakeUppercase #2s\else #2目次\fi% }% \expandafter\def\csname listof#1s\endcsname{% \if@twocolumn\@restonecoltrue\onecolumn% \else\@restonecolfalse\fi% \ifdefined\chapter% \chapter*{\csname list#1name\endcsname}% \else% \section*{\csname list#1name\endcsname}% \fi% \@mkboth{\csname list#1name\endcsname}{}% \@starttoc{#3}% \if@restonecol\twocolumn\fi% }% \expandafter\let\csname l@#1\endcsname\l@figure% % % for cleveref \crefname{#1}{#2}{#2}% \if@english% \crefformat{#1}{##2\textgb{#2 ##1}##3}% \crefmultiformat{#1}% {##2\textgb{#2 ##1}##3}% {, ##2\textgb{#2 ##1}##3}% {, ##2\textgb{#2 ##1}##3}% {, ##2\textgb{#2 ##1}##3}% \else% \crefformat{#1}{##2\textgb{#2##1}##3}% \crefmultiformat{#1}% {##2\textgb{#2##1}##3}% {,##2\textgb{#2##1}##3}% {,##2\textgb{#2##1}##3}% {,##2\textgb{#2##1}##3}% \fi% }% % % e.g. define some new algorithmic float by \newalgfloat \newalgfloat{protocol}{プロトコル}{lop}% %
以下ではこのコードについて何点か説明を加えます。
float 環境の作り方
algorithm 環境のような float 環境*4を新しく定義するには,基本的に次の4つを用います。(1) \floatstyle{style}
この先で定義する float 環境のスタイルを決めます。
(2) \newfloat{type}{placement}{ext}[within]
新しい float 環境を作成するコマンドです。各引数の内容は以下に述べる通りです。
- type ... これで \begin{type} – \end{type} によって環境を使用できるようになります。
- placement ... 環境のデフォルト配置優先度を決めます。htbpなどで指定するやつです。
- ext ... この float 環境だけの目次を作成する際に使われるファイルの拡張子を指定します。
- within ... ここに指定した見出しレベルを含めた番号で環境に番号を振ります。
(3) \floatname{type}{float name}
float name に指定した文字列が type 環境のキャプションに使われます。
(4) \listof{type}{title}
type 環境だけの目次を出力するコマンドです。目次のタイトルが title になります。
ちょっとした改良
基本的には上の4つを使えば float 環境を新しく整備できますが,上記の \listof を使って目次を出力すると,jsclasses 系のフォーマットと統一されません。
そこで,今回は
- \listof を使わずに jsclasses 系の目次出力のコマンド(例えば \listoffigures)を基に \listoftypes を定義する*5。
- \listoftypes によって出力される見出しには,あれば \chapter を使い,なければ \section を使う*6
という作戦を取りました。
1個目は単にコピペすれば良いだけで解決できます。
2個目は説明を割愛しますが,「\ifdefined」という指定されたコマンドが定義されているかどうかで処理を分岐させるコマンドを用い,
\ifdefined\chapter% \chapter*{プロトコル目次}% \else% \section*{プロトコル目次}% \fi%
とすれば対応できます。
↓出力はこんな感じになります。
可変名のコマンド定義のしかた
例えば,foo,bar,baz という3つの float 環境を定義したいときは,\listoffoos,\listofbars,\listofbazs という3つのコマンドを1つずつ定義する必要があります。しかし,これだとコードは複雑になる上,統一した修正を加えたいときにとても面倒です。これらのコマンドの処理内容はほぼ同じですから,コマンド名の一部を変数にして \listof#1s として一般の定義を書けたら凄く楽でしょう。これを実現できるようにするのが \csname ~~ \endcsname です。
\csname ~~ \endcsname
この2つのコマンドで挟まれた文字列はコマンドの名前として処理され,次のコード例に書き込んだように,1つ目の変数に入る内容によって異なるコマンド名を意味することができます*7。\csname listof#1s\endcsname % #1 = foo なら,\listoffoos と等価 % #1 = bar なら,\listofbars と等価
\def にうまく組み込むための \expandafter
今回は \csname listof#1s\endcsname という名前のコマンドを \def によって定義したいのですが,これを\def\csname listof#1s\endcsname...
と書いても,まず「\def\csname」の部分で解釈されてしまうためうまくいきません*8。
正しくは以下のコードのようにコマンドの展開の順番を後回しにする「\expandafter」というコマンドを使用します。
\expandafter\def\csname listof#1s\endcsname{定義内容}% % #1 = foo なら,\def\listoffoos{定義内容} と等価 % #1 = bar なら,\def\listofbars{定義内容} と等価
cleverefパッケージへの対応
最後に参照をうまく取り扱ってくれる cleveref パッケージに対応させてみましょう。今回は\crefname,\crefformat,\crefmultiformat の3つを定義しておきました。これらのコマンドの詳細な説明は公式ドキュメントや他の解説記事に譲り,次の節では英語と日本語のどちらで文章を書いていても対応できるようにコマンドを定義する方法についてまとめます。
公式ドキュメントなど
www.ctan.org
解説記事①
qiita.com
解説記事②
qiita.com
\if@english を用いた定義の切り替え
jsclasses 系のドキュメントスタイルは english オプションを付けることで英語版の体裁に変えることができます。これを用いてもし英語版であれば処理Aを,そうでなければ処理Bを行うというコマンドを作ることができます。\def\hoge{% \if@english% % 処理A % \else% % 処理B % \fi% }
\newalgfloat の使用例
ここまでで冒頭に示した完成コードの構成要素を説明しおわりました。最後に,\newalgfloat を使って新しく algorithm 環境に類似した環境を作ってみます。
……といっても定義は非常にシンプルです*9。
% 環境を使う前に定義しておく \newalgfloat{protocol}{プロトコル}{lop}% % % 環境の出力時 \begin{protocol} \caption{値を交換する} \label{prot:change} \begin{algorithmic}[1] \AlgInput A: $s_{0}$,B: $s_{1}$ \AlgOutput A: $t_{0}$,B: $t_{1}$ \Comment{$t_{i} = s_{1-i}$} \State A sends $s_{0}$ to B, B receives it as $t_{1}$ \State B sends $s_{1}$ to A, A receives it as $t_{0}$ \Return A: $t_{0}$,B: $t_{1}$ \end{algorithmic} \end{protocol}
↓出力例はこんな感じです。
本文中で言及しなかった参考文献
1. 公式ドキュメントCTAN: Package float
2. 公式ドキュメント
CTAN: Package algorithms
3. 公式ドキュメント
CTAN: Package jsclasses
4. 解説記事 —\expandafter について
徹底解説! \expandafter 活用術(キホン編) - Qiita
*1:残念ながら皆さんにとって需要があるかは判然としませんが,少なくとも私には需要があったので紹介することにしました。
*2:なお,動作確認はupLaTeX→dvipdfmxの設定で行っております。
*3:こんなことは皆さんにはどうでもよいと思いますが,第2弾を公開する前にこの記事を書き始めています。
*4:float 環境は figure 環境や table 環境のなかまであり,コンテンツを本文と分離した上でキャプションを加えるなどして出力する環境を指します。
*5:jsclasses 系でないドキュメントスタイルに合わせる場合も,このコマンドの定義を参考にするとうまくいくでしょう。
*6:例えば jsarticle には \chapter というコマンドは定義されていません。したがって,jsbook に合わせて目次を \chapter で出力させると,jsarticle を使ったときにうまく機能しないコードになります。そのためこのような改良を加えました。
*7:半角スペースが入る場所と入らない場所に気をつけてください。\endcsname の前にスペースが入ると正しく処理されません。
*8:\expandafter を挿入しない場合,TeXは「\def」を先に展開し「\def\csname~~」を 「\csname を ~~ と定義しなさい」と解釈します。これはいまやりたいことではありません。いまやりたいのは「\csname ~~ \endcsname」を展開して得られるコマンド名のコマンドを定義することですから,「\def」の展開を後に回す必要があります。したがって「\expandafter」を使います。
*9:複雑な部分を \newalgfloat の内部に押し込んだのでそれはそう。