setcounter 宏的扩展存在问题( \csname 的使用与其定义不匹配)?

setcounter 宏的扩展存在问题( \csname 的使用与其定义不匹配)?

后续行动拆分任意编号项的参考计数器(如子方程式):我试图制作一个通用的任何事物宏,我失败了——请参阅下面的 MWE,了解我得到的错误,基本上是\setcounter{parent#1}{\value{#1}}

\documentclass{article}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{xcolor} % \pagecolor
\usepackage{hyperref}
\pagecolor{yellow!15}
\usepackage{trace}

% modded from {subequations}, `kpsewhich amsmath.sty`
\newcounter{parentlstlisting}% Counter for ``parent lstlisting''; set up manually for all that may be used in subanything
\makeatletter
\newenvironment{subanything}[1][]{%
  \traceon
  \xdef\subanyin{#1}%
  \xdef\dosubany{1}%
  \typeout{subanything: inside '#1'}%
  \ifx#1\undefined%
    \xdef\dosubany{}%
    \typeout{subanything: Didn't get argument: '#1'; skipping start}%
  \else%
    \ifx\subanyin\empty%
      \xdef\dosubany{}%
      \typeout{subanything: Didn't get argument: '#1'; skipping start}%
    \else%
    \refstepcounter{#1}%
    \typeout{subanything: A}%
    \edef\tcsa{theparent\subanyin}%
    \edef\tcsb{the\subanyin}%
    \expandafter\protected@edef\expandafter\csname\tcsa\endcsname{\expandafter\csname\tcsb\endcsname}%
    \typeout{subanything: B}%
    \edef\tcsc{parent#1}%
    \makeatletter%
    % problem here with setcounter:
    % \csname theparentlstlisting\endcsname ->1
    % ! Use of \csname doesn't match its definition.
    % <argument> c
    %             @parentlstlisting
    % \@ifundefined #1->\expandafter \ifx \csname #1
    %                                               \endcsname \relax \expandafter...
    \expandafter\setcounter\expandafter{\tcsc}{\value{#1}}%
    \setcounter{#1}{0}%
    \typeout{subanything: C}%
    \expandafter\def\csname the\subanyin\endcsname{\expandafter\csname theparent\subanyin\endcsname\alph{#1}}%
    \ignorespaces%
    \fi%
  \fi%
}{%
  \ifx\dosubany\empty% ifx #1: ! Illegal parameter number in definition of \endsubanything.: 1
    \typeout{subanything: skipping end}%
  \else%
  \setcounter{\subanyin}{\value{parent\subanyin}}%
  \ignorespacesafterend%
  \fi%
}
\makeatother


\begin{document}

Hello...

\begin{subequations}\label{eq:ex1}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1a}
a = b + c
\end{align}
\end{minipage}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1b}
x = y + z
\end{align}
\end{minipage}
\end{subequations}

\vspace{\baselineskip}
Ref'ing: master eq.~\ref{eq:ex1};
inner a eq.~\ref{eq:ex1a}, inner b eq.~\ref{eq:ex1b} ...

\begin{subanything}[lstlisting] \label{lst:ex1}
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1a,
columns=fixed,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\hspace{1cm}
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1b,
columns=fullflexible,
% basewidth=\tlen,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\end{subanything}

Ref'ing:
inner a listing~\ref{lst:ex1a}, inner b listing~\ref{lst:ex1b} ...

\end{document}

@看起来好像在 at in处中断了c@parentlstlisting- 这应该是荒谬的,因为我们明确地位于\makeatletter?! 内部,我在这里遗漏了什么?

答案1

问题在于这条线

\expandafter\protected@edef\expandafter\csname\tcsa\endcsname{\expandafter\csname\tcsb\endcsname}%

在这里,您正在扩展\tcsa(实际上您不需要这样做),但\csname之前没有扩展\protected@edef。结果是您重新定义了\csname:一件坏事!您需要

\expandafter\protected@edef\csname\tcsa\endcsname{\csname\tcsb\endcsname}%

它首先扩展名称,然后使用\protected@edef正确的名称。我已删除\expandafter定义中另一个不需要(但无害)的内容。

答案2

你得到的错误

Use of \csname doesn't match its definition

是一个明确的信号,表明定义中出现了问题,因为这意味着你已经重新定义 \csname.这发生在

\expandafter\protected@edef\expandafter\csname\tcsa\endcsname{%
  \expandafter\csname\tcsb\endcsname}

因为第一个\expandafter会扩展,第二个也会扩展\tcsa,最后得到

\protected@edef\csname<expansion of \tcsa\endcsname{%
  \expandafter\csname\tcsb\endcsname}

它确实会\csname用一个非常特殊的参数文本重新定义。

正如约瑟夫赖特 (Joseph Wright) 指出的那样,这个问题可以通过省略第二行\expandafter(以及第三行,但实际上没有任何用处)来解决:

\expandafter\protected@edef\csname\tcsa\endcsname{\csname\tcsb\endcsname}

不需要其他的\expandafter,只需要第一个来表示\protected@edef象征性的象征。

但是,您的代码中还存在其他问题。

  1. \ifx#1\undefined\fi是完全错误的;如果你想测试参数是否为空,请使用\if\relax\detokenize{#1}\relax

  2. 您做出的几个定义都是无用的,而且正如约瑟夫赖特所说,扩展的顺序是错误的。

最后,解决方案要简单得多。除非您希望能够嵌套子计数器,否则只需要一个“父子计数器”。此外,\setcounter已经\value扩展了它们的参数。

\documentclass{article}
\usepackage{amsmath}
\usepackage{listings}
\usepackage[colorlinks]{hyperref}

% modded from {subequations}, `kpsewhich amsmath.sty`
\newcounter{parent@@subcounter}% Counter for the parent subcounter

\makeatletter
\newif\if@subanyempty
\newenvironment{subcounter}[1][]{%
  \if\relax\detokenize{#1}\relax
    \@subanyemptytrue
    \typeout{subcounter: Didn't get argument: '#1'; skipping start}%
  \else
    \def\subany@argument{#1}% for the end part
    \@subanyemptyfalse % actually redundant
    \refstepcounter{#1}% step the given counter
    \protected@edef\theparent@@subcounter{\csname the#1\endcsname}%
    \setcounter{parent@@subcounter}{\value{#1}}%
    \setcounter{#1}{0}%
    \ifcsname theH#1\endcsname % for hyperref
      \@namedef{theH#1}{\theparent@@subcounter\alph{#1}}%
    \fi
    \@namedef{the#1}{\theparent@@subcounter\alph{#1}}%
  \fi
  \ignorespaces
}{%
  \if@subanyempty
    \typeout{subanything: skipping end}%
  \else
    \setcounter{\subany@argument}{\value{parent@@subcounter}}%
  \fi
  \ignorespacesafterend
}
\makeatother


\begin{document}

Hello...

\begin{subequations}\label{eq:ex1}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1a}
a = b + c
\end{align}
\end{minipage}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1b}
x = y + z
\end{align}
\end{minipage}
\end{subequations}

\vspace{\baselineskip}
Ref'ing: master eq.~\ref{eq:ex1};
inner a eq.~\ref{eq:ex1a}, inner b eq.~\ref{eq:ex1b} ...

\begin{subcounter}[lstlisting] \label{lst:ex1}
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1a,
columns=fixed,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\hspace{1cm}
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1b,
columns=fullflexible,
% basewidth=\tlen,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\end{subcounter}

Ref'ing:
inner a listing~\ref{lst:ex1a}, inner b listing~\ref{lst:ex1b}, master \ref{lst:ex1}

\end{document}

在此处输入图片描述

这是一个expl3允许嵌套的版本,通过在环境第一次与特定计数器一起使用时定义“父”计数器。

\documentclass{article}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{xparse}

\usepackage[colorlinks]{hyperref}

\ExplSyntaxOn
\NewDocumentEnvironment{subcounter}{o}
 {
  \IfValueT{#1}{ \sdaau_setup_subcounter:n { #1 } }
  \ignorespaces
 }
 {
  \IfValueT{#1}{ \sdaau_restore_subcounter:n { #1 } }
  \ignorespacesafterend
 }

% an expl3 version of \protected@edef
\cs_set_eq:Nc \sdaau_protected_edef:Npn { protected@edef }
\cs_generate_variant:Nn \sdaau_protected_edef:Npn { c }

\cs_new_protected:Npn \sdaau_setup_subcounter:n #1
 {
  \cs_if_exist:cF { c@parent@@#1 }
   {% for the first time a counter is subbed
    \newcounter{ parent@@#1 }
   }
  \refstepcounter{ #1 }
  \sdaau_protected_edef:cpn { theparent@@#1 } { \use:c { the#1 } }
  \setcounter{ parent@@#1 } { \value { #1 } }
  \setcounter{ #1 } { 0 }
  \cs_set:cpn { the#1 }{ \use:c { theparent@@#1 } \alph{#1} }
  \cs_if_exist:cT { theH#1 }
   {
    \cs_set_eq:cc { theH#1 } { the#1 }
   }
 }
\cs_new_protected:Npn \sdaau_restore_subcounter:n #1
 {
  \setcounter{#1}{\value{parent@@#1}}
}
\ExplSyntaxOff


\begin{document}

Hello...

\begin{subcounter}[equation]\label{eq:ex1}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1a}
a = b + c
\end{align}
\end{minipage}
\begin{minipage}{0.45\textwidth}
\begin{align}
\label{eq:ex1b}
x = y + z
\end{align}
\end{minipage}
\end{subcounter}

\vspace{\baselineskip}
Ref'ing: master eq.~\ref{eq:ex1};
inner a eq.~\ref{eq:ex1a}, inner b eq.~\ref{eq:ex1b} ...

\begin{subcounter}[lstlisting] \label{lst:ex1}
%\begin{subcounter}% switch the comments to test
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1a,
columns=fixed,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\hspace{1cm}
\noindent\begin{minipage}[t]{.325\textwidth}
\begin{lstlisting}[basicstyle=\scriptsize\ttfamily,
caption={[short] Some instructions here; the font here is \texttt{\ttdefault}.},
escapechar=!,
showlines=true,
label=lst:ex1b,
columns=fullflexible,
% basewidth=\tlen,
frame=tlrb]
080484c4 <list>:
 80484c4: cmd one
 80484c7: cmd two
 80484ca: cmd three, four
 80484cf: cmd five
 80484d6: cmd six, seven
 80484dd: cmd more than enough
 80484e0: cmd  not_even_joking
\end{lstlisting}
\end{minipage}
\end{subcounter}

Ref'ing:
inner a listing~\ref{lst:ex1a}, inner b listing~\ref{lst:ex1b}, master \ref{lst:ex1}

\end{document}

相关内容