\renewcommandと\RenewDocumentCommandで挙動が変わるのはなぜでしょうか?

\renewcommandと\RenewDocumentCommandで挙動が変わるのはなぜでしょうか?

- Ryuta Kudo の投稿
返信数: 3

\renewcommand\RenewDocumentCommandで挙動が変わるものがあり、理由がよくわからず気になっています。

再現できると思われる最小のソースを以下に示します。所望の出力は添付PDFのものです。

\renewcommandを使った際† は\refを用いて「Listing 1.1に示す」「Listing 2.1に示す」と望み通り参照できるものの、\RenewDocumentCommandを使った際†† は\refすると「Listing 1.0に示す」「Listing 2.0に示す」と番号がおかしくなってしまいます。

\renewcommand{\thelstlisting}{\arabic{section}.\arabic{lstlisting}}とした際

†† \RenewDocumentCommand\thelstlisting{}{\arabic{section}.\arabic{lstlisting}}とした際

ひとまず\RenewDocumentCommandではなく\renewcommandを使うことで対処していますが、この挙動の違いの理由が気になります。

理由がお分かりの方いらっしゃいましたら、ぜひご教示お願いします。

\AtBeginDocument内の記述はhttps://ta-b0.hateblo.jp/entry/2020/08/13/001223を参考にしました。

\documentclass{jlreq}
\usepackage{listings}
\makeatletter
\AtBeginDocument{
\renewcommand{\thelstlisting}{\arabic{section}.\arabic{lstlisting}} % うまくいく
% \renewcommand{\thelstlisting}{\arabic{section}.\protect\arabic{lstlisting}} % うまくいかない
% \RenewDocumentCommand\thelstlisting{}{\arabic{section}.\arabic{lstlisting}} % うまくいかない
\@addtoreset{lstlisting}{section}
}
\makeatother

\begin{document}
\section{hoge}

Listing~\ref{list:hoge}に示す。
\begin{lstlisting}[caption=hoge,label=list:hoge]
print("Hello, LaTeX!")
\end{lstlisting}

\section{fuga}

Listing~\ref{list:fuga}に示す。
\begin{lstlisting}[caption=fuga,label=list:fuga]
print("Oh, TeX...")
\end{lstlisting}
\end{document}

(LuaLaTeX)

Ryuta Kudo への返信

Re: \renewcommandと\RenewDocumentCommandで挙動が変わるのはなぜでしょうか?

- 本田 知亮 の投稿
auxを見ればわかりますよ.
\thelisting に限らずカウンタhogeに対する\thehogeは
robustにすると参照に齟齬が起きるということです.
\labelが発行されたその場での値に展開されないと意味がないです.

具体的には\RenewDocumentCommandの場合,auxには

\newlabel{list:hoge}{{\thelstlisting}{1}{}{}{}}

と展開されないでそのまま書かれるので,
\ref{list:hoge}は結局のところ,\thelstlisting に展開されます.
となると,
「Listing~\ref{list:hoge}に示す。」

「Listing~\thelstliting に示す。」
になります.また
「Listing~\ref{list:hoge}に示す。」をlisting2.1の直後にもっていくと
2.1になるはずです.
本田 知亮 への返信

Re: \renewcommandと\RenewDocumentCommandで挙動が変わるのはなぜでしょうか?

- Ryuta Kudo の投稿

ありがとうございます。.auxのような補助ファイルは見たことがありませんでした。

TeX言語レベルの話、特に展開、RobustやFragileといった事項に関する知識がほぼ無いため、たいへん申し訳ないのですが、十分な理解ができませんでした。

つまるところ、xparseの\RenewDocumentCommandだとRobustなコマンドとなるため、うまく展開?できず、この問題が生じているという認識で正しいでしょうか。

\RenewDocumentCommandでも正常に動作させる(\renewcommand使用時と同様の結果を得る)ためにはどう修正すべきなのでしょうか。

Ryuta Kudo への返信

Re: \renewcommandと\RenewDocumentCommandで挙動が変わるのはなぜでしょうか?

- 本田 知亮 の投稿
どういうと分かりやすいですかね
細かい部分はさておきこんな感じです.

\label{A} というのは「\ref{A}とは\label{A}が発行されたときの直前のカウンタの値である」と定めることといっていいです.
このときの「カウンタの値」というのは,カウンタに対する\the<カウンタ> マクロに保存されている値です.
そしてマクロをそこに保存されている中身そのものに置き換えることを「展開」といいます.

そこでちょっといい直すと
カウンタlstlisting に対して\label{A}を発行するということは
「\ref{A}とは\label{A}が発行された時点での\thelstlistingを展開した値であると定める」ことになります.

ところが,
\RenewDocumentCommandは「展開を抑止する」という機能をもったマクロを作るものであるので,
\thelstlistingを\RenewDocumentCommandで定義すると,
「\ref{A}が\thelstlistingそのものになる」,
つまり「\label{A}を発行した時点での値に「展開」される」という部分が抑止されてしまうのです.
これがおかしなことを引き起こします.

具体的には\ref{A}が\thelstlistingそのものなので,
\ref{A}が使われた時点でのlstlstingの値\thelstlistingになってしまうため,
\ref{A}が使われた場所によって異なりうる(\label{A}で引っ張り出したい値とは限らない)のです.

>\RenewDocumentCommandでも正常に動作させる(\renewcommand使用時と同様の結果を得る)ためにはどう修正すべきなのでしょうか。

無理です.\RenewDocumentCommandを提供するxparse(今はLaTeX2e本体に取り込まれてる)のドキュメントの冒頭に
The xparse package provides a high-level interface for producing document-level commands.
とあります.カウンタの表示形態をいじるのは「document-level」ではなく「design-level」に相当すると思います.
\def で定義するのが妥当でしょうか.