LaTeX で key-value リストを活用する:xkeyval パッケージ

この記事は最終更新日から3年以上が経過しています。

はじめに

LaTeX 命令の一般的な書式では,通常の引数はブレース{},オプション引数はブラケット[]で囲って表現されます.

\command[〈オプション引数〉]{〈引数1〉}{〈引数2〉}

この方針はシンプルですが,引数の数が増えると「何番目の引数に何を書けばいいのか」ということがわかりにくくなってしまうという欠点があります.

一方,graphicx や TikZ といったパッケージを利用したことのある方であれば,〈キー〉=〈値〉のカンマ区切りリスト(key-value リスト)を使用したことがあるのではないでしょうか.

% graphicx における例
\includegraphics[width=5cm, angle=30]{sushi.png}

% TikZ における例
\begin{tikzpicture}[x=1cm, y=1cm, line width=2pt]
\input{snowman.tex}
\end{tikzpicture}

【注意】key-value リストでは先頭・末尾およびカンマと等号の前後の空白はすべて無視されます.これは重要なことなので是非覚えておいてください.また key-value リストの〈値〉の中で,=を使いたい場合は〈値〉全体をブレースで囲って,例えばkey={x=1, y=2}のようにする必要があることも記憶に値するでしょう.

この key-value リストを扱う命令の実装は,最初に紹介したシンプルな形式の引数をとる命令よりも多少煩雑にはなります.しかし key-value リストを利用することには

  • 引数の順番が自由かつ意味が明示的なので書式を覚えやすい
  • グローバルな設定とローカルな設定を分けて実装しやすい
  • 後方互換性を崩さずに機能拡張するのが容易

など多くのメリットがあります.本稿では,そんな key-value リストを扱う命令の作成を支援する xkeyval パッケージの基本的な使い方を解説していきます1

対象読者と記事の方針

本稿の目的は「LaTeX でキーワード引数を使えるようにするための技術について解説すること」ですが,本稿の内容には TeX 言語的要素が多分に含まれます.したがって,本稿の対象読者は基本的には TeX 言語の基礎的な事項を習得している方,中でも LaTeX パッケージを制作する意欲のある方2です.

また,本稿では原則として「まず抽象的な説明を行ってから,具体例を示す」という形をとっています.key-value リストに関わる概念には少々複雑なものもあるので,抽象的な説明が理解できない場合でも立ち止まらずに読み進め,具体例をじっくり眺めるようにするといいかもしれません.

xkeyval パッケージについて

背景

LaTeX でキーワード引数を扱うためのパッケージとして最も基本的なものに keyval があります.これは元々 graphicx の内部で利用するために開発されたもののようですが,現在は LaTeX 必須パッケージ の1つである la­tex-graph­ics の一部として,LaTeX プロジェクトチームによって管理されています.そのため今日では keyval は様々なパッケージで内部的に利用されています.

この keyval パッケージは極めて簡素なものであるため,基本的な使い方を覚えるのは容易であり,さらに色々と工夫さえすればかなり凝った仕様のキーワード引数を実装することも可能です.一方で,keyval パッケージは少々素朴過ぎる嫌いがあり,ちょっと複雑なキーワード引数を実現しようとするとすぐにコードが冗長かつ複雑なものになってしまいます.

そうした欠点を補うため,keyval パッケージを機能拡張したものが xkeyval です.xkeyval は内部的に keyval を読み込んでおり,keyval に対して上位互換です.さらに xkeyval では,通常の命令だけでなくクラスやパッケージのオプションにも key-value リストを利用できるようにするためのしくみが追加されているほか,名前空間の分離をより徹底して行うための工夫等もなされています.

読み込み方法

LaTeX コード中で xkeyval パッケージを利用するためには,\usepackageまたは\RequirePackage命令を用いて xkeyval を読み込む必要があります.パッケージオプションは存在しません.

% 文書ファイル(.tex)等で使用する場合
\usepacakge{xkeyval}

% パッケージファイル(.sty)等で使用する場合
\RequirePacakge{xkeyval}

基本的な考え方

keyval や xkeyval パッケージにおいてキーワード引数を実装するための大まかな流れをまとめると次のようになります.

  1. キーを定義する
  2. キーを設定する
  3. 目的の処理を実行する

はじめのキーを定義するというのは,基本的な key-value リストの構成要素 〈キー〉=〈値〉 における〈キー〉の文字列を予め定めておくということです.ここで,次のキーを設定するという操作が行われた際の処理も合わせて記述しておきます.具体的なキーの定義方法については次節にて詳述します.

次にキーを設定するという操作ですが,これが「実際に key-value リストを読み込む」段階にあたります.すなわち,リストから〈値〉を受け取り,その情報を利用してキーの定義時に〈キー〉と対応付けられた処理を行います.この処理はすべて\setkeys命令によって行われます.

そして,ここまでに行われた前処理の情報を利用して目的の処理を実行するというのが一般的な流れです3

キーの定義

汎用キー定義命令

最初に\define@key命令について説明します.xkeyval パッケージの公式ドキュメントでは「通常キー(ordinary keys)の定義命令」と呼称されていますが,この命令がこれから紹介するすべてのキー定義命令の基本形であり,その気になればこの命令1つでさまざまな種類のキーを実装することが可能なので,本稿では汎用キー定義命令と呼ぶことにします4

以下に示すのが\define@key命令の完全な書式です5

\define@key[〈接頭辞〉]{〈ファミリー〉}{〈キー〉}[〈デフォルト値〉]{〈処理〉}

オプション引数については後述することにして,先に必須の引数について説明します.1つ目の引数には定義する〈キー〉の属する〈ファミリー〉を指定するものです.のちに\setkeys命令を用いて定義したキーの設定を行う際にも,同じ〈ファミリー〉を指定する必要があります.すなわち〈ファミリー〉は名前空間をつくるためのしくみで,役割上関連のある〈キー〉を1つのファミリーにまとめるといいでしょう.

2つ目の引数には定義する〈キー〉を記入します.この〈キー〉にはかなり多くの文字種を使うことができますが,特に理由がない限り英数字(および半角スペース)のみからなる文字列にすることを推奨します6

そして3つ目の引数には〈キー〉に紐付けられる〈処理〉,つまり\setkeys命令でキーの設定を行う際に実行されるコードを記述します.この〈処理〉の中で#1を書くと,その部分は key-value リストで記述した〈値〉に置き換えられます.なお,オプション引数として〈デフォルト値〉を設定しておくと,=〈値〉の部分を省略したとしても#1〈デフォルト値〉に置換されます.この〈デフォルト値〉に関してはプリセットキーに関する節でまた説明します.

通常キーの実装例

ここでは汎用キー定義命令\define@keyを用いた通常キーの実装例を示します.その他の種類のキーの実装例については,次節以降でそれぞれ専用の定義命令の使い方を説明するときに合わせて提示することにします.

さっそくdependという通常キーをもつ\myare命令を定義してみます.なお,以降本稿に掲載するすべてのコード例では,@が制御綴中で使用できる状況(@のカテゴリーコードが11)であることを仮定しています.

% スイッチの宣言
\newif\ifdepend@exist

% キーの定義
\define@key{are}{depend}{\def\are@depend{#1}\depend@existtrue}

% \myare 命令の定義
\def\myare#1#2{%
  % 初期化:\ifdepend@exist スイッチを OFF
  \depend@existfalse
  % キーを設定
  \setkeys{are}{#2}%
  % アレな内容の出力
  #1%
  \ifdepend@exist
    \space\textsc{on}\space\are@depend はもっと
  \else%
  \fi アレ}

上で定義した\myare命令は2つの省略不能な引数をもちます.1つ目の引数には「〇〇はアレ」というフレーズの〇〇に入れたい文字列を指定します.2つ目の引数はキーワード引数のリストを渡すところですが,上の例ではキーがdependしか定義されていないので何も書き込まないかdepend=〈値〉を書き込むかの2択ということになります.

それでは実際に\myare命令を使ってみます.

\myare{\TeX}{}\par
\myare{\TeX}{depend=\LaTeX}

ordinary.png

\define@key\setkeysの内部動作

\define@keyは内部的に\KV@〈ファミリー〉@〈キー〉という引数を1つとるマクロを定義します.このマクロがとる引数というのが key-value リストの形で与えられた〈値〉です.また,\define@keyに与えた〈処理〉がこのマクロの定義内容となります.すなわち\myare命令の例では,\define@keyによって次のような定義がなされたことになります.

% \define@key{myare}{depend}{\def\are@depend{#1}\depend@existtrue} の内部動作
\def\KV@myare@depend#1{\def\are@depend{#1}\depend@existtrue}

一方の\setkeys命令は,与えられた key-value リストをもとに上記のマクロを呼び出します.

% \setkeys{myare}{depend=\LaTeX} の内部動作
\KV@myare@depend{\LaTeX}

これにより,\ifdepend@existスイッチが ON になり,さらに\are@depend\def\are@depend{\LaTeX}のように定義されアレな内容が出力される準備が整うというわけです.

ところで,\define@keyによって内部的に定義されるマクロの接頭辞はKVとなっていますが,これは keyval パッケージで定義される\define@keyとの互換性を維持するための仕様です.xkeyval の\define@keyがとる最初のオプション引数で〈接頭辞〉を指定すると,これをKV以外の文字列に置き換えることも可能です.ただし,その場合は\setkeysでキーの設定を行う際にも同じ〈接頭辞〉を指定する必要があります.

% 〈接頭辞〉を指定してキーの定義(内部的に \my@are@depend が定義される)
\define@key[my]{are}{depend}{\def\are@depend{#1}\depend@existtrue}

% 同じ〈接頭辞〉を指定してキーを設定(内部的に \my@are@depend{\LaTeX} が実行される)
\setkeys[my]{are}{depend=\LaTeX}

プリアンブル等でマクロを定義する場合はそれほど気にする必要はないですが,xkeyval を用いてパッケージを開発するというような場合には,接頭辞に KV を用いると他のパッケージとファミリー名およびキーが被ってしまった際に衝突を起こす可能性があります.そのため〈接頭辞〉にはパッケージ名に因んだ文字列を指定するなどして衝突のリスクを下げるようにするといいでしょう.

コマンドキー定義命令

key-value リストで受け取った〈値〉はしばしば一旦制御綴に格納されて,あとから参照されます.このように,受け取った値を何らかの制御綴に格納するタイプのキーを xkeyval の用語でコマンドキー(command keys)といいます.

さきほどの\myare命令におけるdependキーは受け取った〈値〉\are@dependに格納しているのでコマンドキーの1種とみなすことができます.すなわちコマンドキーは〈処理〉\def命令を用いたマクロ定義を明示的に記入することによって\define@key命令を用いて実装することが可能です.しかし,xkeyval パッケージにはより簡潔にコマンドキーを定義できる\define@cmdkeyという専用の命令が用意されています.

\define@cmdkey[〈接頭辞〉]{〈ファミリー〉}[〈マクロ接頭辞〉]{〈キー〉}[〈デフォルト値〉]{〈処理〉}

\define@cmdkeyでも〈ファミリー〉〈キー〉〈デフォルト値〉の取り扱い方は\define@keyとまったく同様です.一方で,\define@cmdkeyを使用した場合は〈処理〉に何も記述しなくても以下のようなマクロ定義が自動的に行われます7

\def\cmdKV@〈ファミリー〉@〈キー〉{#1}  % '#1' は〈値〉(または〈デフォルト値〉)に置換される

ただし〈マクロ接頭辞〉を指定した場合,自動的に定義されるマクロの形が以下のように変化します.

\def\〈マクロ接頭辞〉〈キー〉{#1}  % 〈マクロ接頭辞〉と〈キー〉の間に @ がない点に注意 

\define@cmdkey命令を用いた場合,\myaredependキーは次のように実装することができます.

% \define@cmdkey 命令による depend キーの実装
\define@cmdkey{myare}[are@]{depend}{\depend@existtrue}

これだけではあまり恩恵が感じられませんが,実際のコマンドキーではマクロに〈値〉を代入する以外の〈処理〉が必要ないというケースも多々あります8.こうした場合,\define@cmdkeys命令を使うことで複数のコマンドキーを簡潔な記述で定義することが可能です.

\define@cmdkeys[〈接頭辞〉]{〈ファミリー〉}[〈マクロ接頭辞〉]{〈キーのリスト〉}[〈デフォルト値〉]

\define@cmdkeys命令では〈値〉をマクロに代入する以外の処理をキーと紐付けることはできませんが,大量のシンプルなコマンドキーを定義する場合にその威力を発揮します.

% \define@cmdkeys 命令の使用例
\define@cmdkeys{example}[exp@]{first, second, third}

% 上記を \define@key 命令で実装した場合
\define@key{example}{first}{\def\exp@first{#1}}
\define@key{example}{second}{\def\exp@second{#1}}
\define@key{example}{third}{\def\exp@third{#1}}

選択キー定義命令

これまでに紹介したキーはいずれもユーザが入力した任意の文字列を値として利用するタイプのものでしたが,そうではないキーというものも考えることができます.その1つが選択キーで,これは予め定められたいくつかの選択肢の中から,ユーザが1つを選択して値として指定するというものです.

xkeyval パッケージでこの選択キーを実装するために用意されているコマンドが\define@choicekeyです.

\define@choicekey[〈接頭辞〉]{〈ファミリー〉}{〈キー〉}[〈保存用制御綴〉]{〈選択肢〉}[〈デフォルト値〉]{〈処理〉}

上記書式のうち〈接頭辞〉〈ファミリー〉〈キー〉〈デフォルト値〉の取り扱いについてはこれまでの場合と同様なので説明を省略します.

残りの必須引数のうち〈選択肢〉にはユーザが指定することのできる選択肢をカンマ区切りのリストで列挙します.オプション引数〈保存用制御綴〉には1つまたは2つの制御綴(マクロ)を記入することができます.このうち1つ目の制御綴にはユーザが指定した文字列そのものが格納されます.そして,2つ目の制御綴には,ユーザの指定した文字列が〈選択肢〉中の n 番目の選択肢と一致するとき n1 の値が保存されます(どの選択肢とも一致しない場合は 1 が格納されます).

さて,最後の引数〈処理〉では,これまで同様#1を書き込むとユーザの入力そのものを受け取ることが可能ですが,選択キーを実装するにあたっては,この入力そのものを利用するよりも,〈保存用制御綴〉で2つ目に指定した制御綴を利用して\ifcaseによる条件分岐を行う方が簡単です.

なお,この\define@choicekeyにはいくつかバリエーションが存在します.

1つは後ろに*オプションをつけた形の\define@choicekey*で,この場合〈保存用制御綴〉に指定した1つ目の制御綴へのユーザ入力の保存や,2つ目の制御綴に格納する数字の決定を行う際の判定が,ユーザ入力の文字列をすべて小文字化してから行われるようになります.

もう1つは後ろに+オプションを付けた形の\define@choicekey+で,この場合は〈処理〉が2つに分かれて次のような書式となります.

\define@choicekey+[〈接頭辞〉]{〈ファミリー〉}{〈キー〉}[〈保存用制御綴〉]{〈選択肢〉}[〈デフォルト値〉]{〈処理1〉}{〈処理2〉}

ここで〈処理1〉はユーザ入力が選択肢中に存在する(正しい)場合に実行され,〈処理2〉はユーザ入力が選択肢中に存在しない(不正な)場合に実行されます.\define@choicekey命令は*オプションと+オプションを同時に指定する\define@choicekey*+の形でも使用可能です.

% 選択キーの定義
\define@choicekey*+{choice}{are}[\val\nr]{tex,latex,tikz}{%
  \ifcase\nr\relax
    \TeX
  \or
    \LaTeX
  \or
    Ti\textit{k}Z
  \fi
  はアレ}{\val はアレではありません}

% \myarechoice 命令の定義
\def\myarechoice#1{\setkeys{choice}{are=#1}}

% \myarechoice 命令の使用
\myarechoice{TeX}\par
\myarechoice{ROFF}

choice.png

真偽キー定義命令

選択キーの特殊なものとして真偽キーというものが考えられます.すなわち選択キーのうち選択肢がtruefalseの2択である場合,これを真偽キーと呼ぶことにします.xkeyval パッケージではこの真偽キーを定義するために\define@boolkeyというコマンドがあります.

\define@boolkey[〈接頭辞〉]{〈ファミリー〉}[〈マクロ接頭辞〉]{〈キー〉}[〈デフォルト値〉]{〈処理〉}

\define@boolkey命令では,ユーザの入力は常にすべて小文字化してからtrueまたはfalseと一致しているかの判定が行われます.

\define@boolkeyを使用すると,\if〈接頭辞〉@〈ファミリー〉@〈キー〉という形のスイッチが作成されます.そして,\setkeys命令の実行時にtrueが指定されていた場合には\iftrueが,falseが指定されていた場合には\iffalseが代入された状態になります.ただし,〈マクロ接頭辞〉が指定されている場合,定義されるスイッチは\if〈マクロ接頭辞〉〈キー〉の形となります.

ところで,\define@boolkeyではユーザの入力がtrueともfalseとも一致しない場合には〈処理〉は実行されず,当該キーは単に無視されます.一方,\define@boolkeyにも\define@choicekeyのように+オプションが存在し,その場合には書式が以下のように変化して,ユーザ入力がtrueまたはfalseと一致する場合は〈処理1〉,そうでない場合は〈処理2〉が実行されることになります.

\define@boolkey+[〈接頭辞〉]{〈ファミリー〉}[〈マクロ接頭辞〉]{〈キー〉}[〈デフォルト値〉]{〈処理1〉}{〈処理2〉}

また,\define@boolkeys命令を使用すると,一度に複数の真偽キーを定義することが可能です.

\define@boolkeys[〈接頭辞〉]{〈ファミリー〉}[〈マクロ接頭辞〉]{〈キー〉}[〈デフォルト値〉]

キーのチェックと無効化

キーが存在するかチェックする

\key@ifundefined命令を用いると,特定のキーが定義されているかどうかチェックすることができます.

\key@ifundefined[〈接頭辞〉]{〈ファミリーのリスト〉}{〈キー〉}{〈未定義の場合の処理〉}{〈定義済みの場合の処理〉}

〈ファミリーのリスト〉には複数のファミリーをカンマ区切りで指定することができます.指定した〈キー〉〈ファミリーのリスト〉に与えたファミリーのいずれかに存在する場合は〈定義済みの場合の処理〉として指定した処理が実行され,そうでない場合には〈未定義の場合の処理〉が実行されます.

キーの無効化

一度定義したキーは,\disable@keys命令を用いると無効化することが可能です.

\disable@keys[〈接頭辞〉]{〈ファミリー〉}{〈キーのリスト〉}

これにより,以降では指定したキーが使用できなくなります.なお〈キーのリスト〉には無効化したいキーをカンマ区切りで複数指定することができます.

\setkeys命令の詳細

\setkeys命令については既に簡単な説明をしてありますが,ここでさらに詳細な事柄について述べておきます.まず,\setkeys命令の完全な書式を紹介します.

\setkeys[〈接頭辞〉]{〈ファミリーのリスト〉}[〈処理しないキーのリスト〉]{〈key-value リスト〉}

\setkeys命令は〈key-value リスト〉で指定された〈キー〉のうち

  • 〈ファミリーのリスト〉中のいずれかの〈ファミリー〉で定義されている
  • 〈処理しないキーのリスト〉に含まれていない

ものについてそのキーの設定を行います(つまり\〈接頭辞〉@〈ファミリー〉@〈キー〉が実行されます).

ここで〈key-value リスト〉〈ファミリーのリスト〉中のどの〈ファミリー〉でも未定義の〈キー〉があるとエラーが発生しますが,*オプション付きの\setkeys*を用いるとそのような場合でもエラーを起こさないようにすることができます9

一方,指定された〈キー〉〈ファミリーのリスト〉中の複数の〈ファミリー〉で定義されている場合には1つ目の〈ファミリー〉についてこの処理が行われます(したがって〈ファミリーのリスト〉中の記述順序には意味があります).この挙動は+オプション付きの\setkeys+を用いると〈ファミリーのリスト〉中のすべての〈ファミリー〉で定義されている〈キー〉の設定を行うように変更することも可能です.

また,例によって*オプションと+オプションは\setkeys*+のように併用することも可能です.

ポインタ

xkeyval には一度指定された〈値〉を保存して別のキーへの値として再利用するためのポインタというしくみがあります.これは,素朴には\setkeysに与える key-value リストの中で\savevalue命令によって値を保存し,\usevalue命令によってその値を呼び出す機能です.以下に具体例を示します.

% キーの定義
\define@cmdkeys{ltx}[ltx@]{format, old, new}
% キーの設定
\setkeys{ltx}{
  \savevalue{format}=\LaTeX,
  old=\usevalue{format} 2.09,
  new=\usevalue{format}3}
% 新旧 LaTeX の出力
古い\ltx@old と新しい\ltx@new

pointer.png

ここで\savevalueおよび\usevalue\setkeys命令の中でしか使用できません.\usevalueの引数に指定した〈key〉に値が保存されていない場合はエラーとなるので注意してください.

さて,上記の例をマクロの形にしなかったのは,この機能を単純に\savevalue\usevalueによって使用する機会はあまりないと思われるからです.もっと実用的なポインタのインターフェースとして,xkeyval には\savevalueによる値の保存操作を自動化する機能が用意されています.

\savekeys[〈接頭辞〉]{〈ファミリー〉}{〈キーのリスト〉}

この\savekeys命令によって指定した〈キー〉は,使用する度に\savevalueを明示的に書かなくても値が保存されるようになります.

また\savekeysによって値を保存するように指定した〈キー〉は,\delsavekeys命令によって値を保存しないように戻すことが可能です.

\delsavekeys[〈接頭辞〉]{〈ファミリー〉}{〈キーのリスト〉}

さらに\unsavekeys命令を用いると,キーを1つ1つ指定するのではなくファミリーに属するキー全体に対して一括でこの無効化を行うこともできます.

\unsavekeys[〈接頭辞〉]{〈ファミリー〉}

プリセットキー

\define@key命令などで〈デフォルト値〉が指定されている場合,キーの使用時に=〈値〉の記入を省略することができます.具体例として,次のような命令を考えてみます.

% キーの定義
\define@key{trio}{engine}[\TeX]{\def\are@engine{#1}}
\define@key{trio}{format}[\LaTeX]{\def\are@format{#1}}
\define@key{trio}{package}[Ti\textit{k}Z]{\def\are@package{#1}}

% \myaretrio 命令の定義
\def\myaretrio#1{%
  % キーを設定
  \setkeys{trio}{#1}%
  % アレな内容の出力
  \are@engine と\are@format と\are@package はアレ}

\myaretrio命令における3つのキー(engine, format, package)にはすべて〈デフォルト値〉が設定されているので,それぞれ値を省略して使うことが可能です.

% すべてデフォルト値を利用する場合
\myaretrio{engine, format, package}

% 一部のキーについてのみ値を指定する場合
\myaretrio{engine, format={plain \TeX}, package}

preset.png

〈デフォルト値〉を設定しておくと多少労力の節約につながりますが,上記\myaretrio命令の3キーワード引数などは明らかに常に指定しなければならないものです10.この場合〈デフォルト値〉を利用する場合には〈キー〉すら記入せずともキーの設定が行われるようにしたいところです.

xkeyval では,そのような要望に応えるためにプリセットキー(Presetting keys)というしくみが用意されています.すなわちプリセットキーとは\setkeysによるキー設定時に key-value リストに〈キー〉が記述されるか否かを問わず常に設定が行われるキーを指します.

既存のキーをプリセットキーとして扱えるようにするには\presetkeys命令を使用します.

\presetkeys[〈接頭辞〉]{〈ファミリー〉}{〈前処理する key-value リスト〉}{〈後処理する key-value リスト〉}

これにより\setkeys〈ファミリー〉に属するキーの設定を行う際,ユーザが\setkeysの引数に入力した key-value リストよりも前に〈前処理する key-value リスト〉が処理され,さらにユーザ入力の key-value リストの処理後に〈後処理する key-value リスト〉が処理されます.

したがって先の例では

% プリセットキーの設定
\presetkeys{trio}{engine, format, package}{}

のようにプリセットキーを設定しておけば

% すべてデフォルト値を利用する場合
\myaretrio{}

% 一部のキーについてのみ値を指定する場合
\myaretrio{format={plain \TeX}}

でまったく同じ出力を得ることができるようになります.

なお\presetkeys命令を同じ〈ファミリー〉について複数回実行した場合には設定は順次更新されていきます.

また,一度\presetkeysでプリセットキーに指定した〈キー〉を普通の(プリセットキーでない)キーに戻すには\delpresetkeysを使用します.

\delpresetkeys[〈接頭辞〉]{〈ファミリー〉}{〈前処理キーのリスト〉}{〈後処理キーのリスト〉}

さらに\unpresetkeys命令を用いると,キーを1つ1つ指定するのではなくファミリーに属するキー全体に対して一括でこの無効化を行うこともできます.

\unpresetkeys[〈接頭辞〉]{〈ファミリー〉}

パッケージオプション

xkeyval パッケージには通常のマクロだけでなくクラスやパッケージのオプションでも key-value リストを利用できるようにするためのしくみがあります11.元々,パッケージオプションを扱うためのしくみと key-value リストを扱うためのしくみはかなり似ているので,「普通のパッケージオプション」を実装したことのある人ならばその方法の習得は容易でしょう.ただし,ここでは念のため普通のパッケージオプションの実装方法も簡単に復習しておきます.

普通のパッケージオプション

クラスやパッケージで使用可能なオプションは,まず LaTeX の\DeclareOption命令によって宣言されている必要があります.この命令の書式は次の通りです.

\DeclareOption[〈オプション〉]{〈処理〉}

このマクロによって〈オプション〉に対して〈処理〉が関連付けられます.その上で,ここで関連付けられた〈処理〉を実際の実行に移します.これは多くの場合2段階に分けて行われます.具体的には,まず\ExecuteOptions命令によって先に “デフォルト” のオプションを実行します.

\ExecuteOptions{〈オプションのリスト〉}

この命令は〈オプションのリスト〉に直接記述した〈オプション〉に関連付けられている〈処理〉を実行するので,結果として〈オプションのリスト〉に書かれたオプションがデフォルトとして適用された状態になります.

そして最後に\ProcessOptions命令によって\usepackageのオプション引数等でユーザが指定したオプションの〈処理〉を実行する,というのが一連の流れです12

\ProcessOptions\relax

key-value リストのパッケージオプション

key-value リストを扱うパッケージオプションを作る方法も基本的には「普通のパッケージオプション」と変わりません.ただし,命令はすべて末尾にXの付くもの(\DeclareOptionX, \ExecuteOptionsX, \ProcessOptionsX)を用います.

オプションの宣言

key-value リスト式のパッケージオプションを宣言するには\DeclareOptionX命令を使用します.もう察しがついていることと思いますが,この命令は\define@key\DeclareOptionの機能を併せ持つような命令で,次のようなちょっと変わった書式をもちます.

\DeclareOptionX[〈接頭辞〉]<〈ファミリー〉>{〈キー〉}[〈デフォルト値〉]{〈処理〉}

それぞれの引数の意味や取り扱いは,基本的に\define@keyと同様です.ここで〈キー〉はパッケージオプションとして使えるもので,〈処理〉中において記述される#1は当該キーに〈キー〉=〈値〉として与えられた値に置換されます.

なお\DeclareOptionXにおける<〈ファミリー〉>は変則的な形をしていますが省略可能なオプション引数です.この〈ファミリー〉はパッケージオプションと共通の〈キー〉を通常のマクロや環境でも利用したい場合に指定します.それ以外の場合は特に記入しなくていいでしょう(その場合は自動的にデフォルトのファミリー名が割り当てられます).

オプションの処理

一方 key-value 式のパッケージオプションは\ExecuteOptionsX\ProcessOptionsXによって処理されます.まず一般的には\ExecuteOptionsXによってデフォルトのオプションの設定が行われます.この命令はもちろん\setkeys\ExecuteOptionsの間の子のような機能を持ちます.

\ExecuteOptions[〈接頭辞〉]<〈ファミリーのリスト〉>[〈処理しないキーのリスト〉]{〈key-value リスト〉}

今度の<〈ファミリーのリスト〉>も省略可能なオプション引数です.\setkeysと同じ要領で〈key-value リスト〉に含まれる〈キー〉が処理されて,デフォルトオプションとして設定されます.

最後にユーザが指定したオプションを\ProcessOptionsXで処理すれば key-value リストを使用したパッケージオプションの設定は完了です.

\ProcessOptionsX[〈接頭辞〉]<〈ファミリーのリスト〉>[〈処理しないキーのリスト〉]

おわりに

xkeyval パッケージの豊富な機能の中でも,特に有用と思われる部分を厳選して解説したつもりでしたが,思った以上にボリュームのある記事になってしまいました.これはそれだけ xkeyval が機能豊富なパッケージであるということの証左でもあります.

皆さんも是非,この強力な xkeyval パッケージを利用して key-value リストを扱うステキな LaTeX マクロを作ってみてください.


  1. 本稿は2016年11月現在最新の xkeyval v2.7a のドキュメントに基づいて執筆されています. 

  2. LaTeX のパッケージ制作に必要な基本的な知識については TeX ユーザの集い2016の一般講演『パッケージ制作のすゝめ』の発表資料にまとめてあります. 

  3. 後述するようにキーに紐付けられる処理は様々なので,必ずキーの設定を行ってから目的の命令が実行されるというわけではありません.キーの設定と目的の処理の実行が同時である場合や,処理の一部が実行されてからキーが設定されるようなケースも考えられます. 

  4. 実際,keyval パッケージにはキー定義命令はこの1種しか存在しないため,変種を含むすべてのキーワード引数を\define@keyで実装する必要があります. 

  5. keyval パッケージの\define@key命令には1つ目のオプション引数[〈接頭辞〉]は存在しません. 

  6. 特に@を含めると xkeyval の実装上本来であれば名前空間の分離されている(〈接頭辞〉または〈ファミリー〉の異なる)キーと衝突を起こすリスクを生じるので,極力避けるべきです. 

  7. これは〈接頭辞〉が指定されていない場合の形です.〈接頭辞〉を指定した場合KVの部分は指定した〈接頭辞〉に置換されます. 

  8. \myare命令のdependキーも予め\def\are@depend{}のように初期化しておき,キーの設定後に\ifx\are@depend\@emptyを用いて条件分岐を行うなどの工夫をすることで\ifdepend@existスイッチを利用せずに実装することが可能です. 

  9. このとき未定義の〈キー〉(およびそれに与えられた〈値〉)は単に無視されるのではなく,それらを集めた key-value リストが\XKV@rmという制御綴の中に格納されます.この key-value リストは\setrmkeys命令によってさらに処理を行うことも可能です. 

  10. 前述の\myaretrio命令の定義では,キーの指定を省略するとそれが初回使用時である場合には\are@engine等の命令が未定義で使用されるためエラーとなり,2回目以降の使用である場合には前回使用した値が引き継がれるという非常に厄介な挙動を示すことになります. 

  11. 話を単純化するため,本稿ではクラスオプションを扱う話は省略します. 

  12. \ProcessOptions*オプション付きで\ProcessOptions*という形で使用することも可能なので(オプションの処理順が異なるようです)*がないことを明示するためには\relax命令を後置して使うのが一般的です. 

wtsnjp
自然言語処理の研究をしています.TeX/LaTeX ユーザ.
https://wtsnjp.com
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした