![setcounter 宏的扩展存在问题( \csname 的使用与其定义不匹配)?](https://linux22.com/image/294672/setcounter%20%E5%AE%8F%E7%9A%84%E6%89%A9%E5%B1%95%E5%AD%98%E5%9C%A8%E9%97%AE%E9%A2%98%EF%BC%88%20%5Ccsname%20%E7%9A%84%E4%BD%BF%E7%94%A8%E4%B8%8E%E5%85%B6%E5%AE%9A%E4%B9%89%E4%B8%8D%E5%8C%B9%E9%85%8D%EF%BC%89%EF%BC%9F.png)
后续行动拆分任意编号项的参考计数器(如子方程式):我试图制作一个通用的任何事物宏,我失败了——请参阅下面的 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
象征性的象征。
但是,您的代码中还存在其他问题。
\ifx#1\undefined\fi
是完全错误的;如果你想测试参数是否为空,请使用\if\relax\detokenize{#1}\relax
您做出的几个定义都是无用的,而且正如约瑟夫赖特所说,扩展的顺序是错误的。
最后,解决方案要简单得多。除非您希望能够嵌套子计数器,否则只需要一个“父子计数器”。此外,\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}