\ref とかの相互参照機能周りのLaTeXカーネル実装は、最近かなり頻繁に変わっているので、自分は
— 某ZR(ざんねん🙃) (@zr_tex8r) 2023年6月28日
「コワくて😱&メンドクサくて😖手を出せない」
という気持ちだったりする。#TeX #TeXLaTeX #TeX言語 https://t.co/t1CtXqcIlG
じゃあどうすればいいかというと、もし
— 某ZR(ざんねん🙃) (@zr_tex8r) 2023年6月28日
「既存の相互参照の処理自体に手を加えたいのでは*なくて*別の系統のものを作りたい」
という話であれば、zrefパッケージが使えるはず。
zrefの機構はカーネルとは独立なので、カーネルの版やhyperrefの影響を受けない。#TeX #TeX言語https://t.co/4G6W4XhSYO
というわけで、実際にやってみた。
問題の整理
元ネタ。
- マークテスト作成に当たって「marksheet.sty」の導入(TeX Forum)
- 問題の「marksheetパッケージ」の配布元はココ。
LaTeXでマークシート式試験(nkhrlab~)
某マクロツイーターのzref関連の解説記事。
実装
変更部分のコードのみを示す。
%%↓★以下を追加 \RequirePackage{zref} \zref@newprop{countermark}[1]{0} \zref@newlist{marksheet} \zref@addprop{marksheet}{countermark} %%↓\markboxletter の実装を修正 \newcommand{\markboxletter}[2][]{% \stepcounter{countermark}% \ifx{#1% }{}{}% \else \ExpandArgs{nx}\zref@setcurrent{countermark}{\thecountermark}%★変更 \zref@labelbylist{#1}{marksheet}%★変更 \fi \katakana{countermark}% \setcounter{itermark}{1}% \whiledo{\value{itermark}<#2}{% \stepcounter{countermark}% \katakana{countermark}% \addtocounter{itermark}{1}% }% } %%↓\refmarkboxletter の実装を修正 \newcommand{\refmarkboxletter}[2][]{% \setcounter{tempcountermark}{\zref@extract{#1}{countermark}}%★変更 \setcounter{itermark}{0}% \whiledo{\value{itermark}<#2}{% \katakana{tempcountermark}% \stepcounter{tempcountermark}% \addtocounter{itermark}{1}% }% }
変更内容は以下の通り。
zrefする場合の問題点
この実装の中で、特にTeX言語初級者にとって一番難しいと思われるのは次の箇所である。
\ExpandArgs{nx}\zref@setcurrent{countermark}{\thecountermark}%★変更
ここでは「LaTeXのcountermarkカウンタ」の現在の値を「zrefのcountermarkプロパティ」に設定しようとしている。ところが、zrefのプロパティは「トークン列を格納する」ものであるため、カウンタの現在の値(を表すトークン列)を得ようとすると、何らかの展開制御が必要になってしまう。具体的には次のようなコードを実行する必要がある。
\zref@setcurrent{countermark}{【\thecountermark の完全展開】}
TeX言語の展開制御に慣れている人であれば、次のような「踏み台パターン」を用いたコードを書くのは容易であろう。
\edef\next{% \noexpand\zref@setcurrent{countermark}{\thecountermark}} \next
しかし、所望の展開パターンからこのコードへ到達する過程は直観的ではなく、TeX言語初級者にとって大変な困難を伴うものであることは間違いない。zrefは別に「初級開発者向け」のパッケージではないので仕方がないことではあるが、もう少しどうにかならないものだろうか。
そこでここでは「\ExpandArgs を使う」という方法を採用してみた。\ExpandArgs
は2022-06-01版のカーネルで追加された命令で「expl3の直観的な展開制御をTeX言語のコードで使えるようにする」ためのものである。今の場合は次のように考える。
- 以下の
\zref@setcurrent
の呼出の引数について、展開制御をしたい。
\zref@setcurrent{countermark}{\thecountermark}
- 第1引数の
countermark
はそのままにしたい →n
指定 - 第2引数の
\thecountermark
は完全展開したい →x
指定 - 従って、
\ExpandArgs{nx}
を\zref@setcurrent
の前に置けばよい。
その結果が本節の冒頭に示したコードになる。
\ExpandArgs{nx}\zref@setcurrent{countermark}{\thecountermark}%★変更
まとめ
- やっぱりzrefパッケージは便利
- 初級者が展開制御したい場合は
\ExpandArgs
が便利かもしれない