新环境中的 csname

新环境中的 csname

我使用此代码(示例)定义了一个新环境:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{amsthm}

\newenvironment{myEnv}[1][default]
{
   \pushQED{#1}
   \expandafter\csname x#1 \endcsname
}
{    \expandafter\csname endx\popQED \endcsname    }

\newenvironment{xdefault}{}{}

\begin{document}

\begin{myEnv}
\end{myEnv}

\end{document}

我稍后需要这个来使用参数调用特定环境。但我在 Overleaf 中收到以下错误:

Missing \endcsname inserted.

<to be read again> 
               \begingroup 
l.35 \end{myEnv}

The control sequence marked <to be read again> should
not appear between \csname and \endcsname.
-----------
Extra \endcsname.

\endmyEnv ...after \csname endx\popQED \endcsname 

l.35 \end{myEnv}

I'm ignoring this, since I wasn't doing a \csname.

据我所知,csname/endcsname 应该是正确的,我没有看到错误,所以我不知道是什么原因造成的。当我搜索错误时,我只找到与我的示例无关的主题。

有人知道如何解决这个问题吗?

答案1

尝试类似

\documentclass{article}
\usepackage[utf8]{inputenc}

% make sure \tssavedarg is not taken already
\newcommand*{\tssavedarg}{}
\newenvironment{myEnv}[1][default]
  {\def\tssavedarg{#1}%
   \csname x#1\endcsname}
  {\csname endx\tssavedarg\endcsname}

\newenvironment{xdefault}{XX}{YY}

\begin{document}

\begin{myEnv}
A
\end{myEnv}

\end{document}

\pushQED\popQED专门用于 中的 QED 处理,amsmath它们似乎实现了完整的 QED 堆栈,而您并不需要。特别\popQED是 不会简单地扩展到 中保存的内容\pushQED,因此它无法\csname ... \endcsname按预期在 中工作。

#1在您的情况下,只需在普通宏中保存\def,然后通过调用它来检索宏值就足够了。

有关的为什么环境的结束代码不能包含参数?

/LaTeX3 解决方案xparse

\usepackage{xparse}
\NewDocumentEnvironment{myEnv}{O{default}}
  {\csname x#1\endcsname}
  {\csname endx#1\endcsname}

或者甚至更多expl3-y

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{myEnv}{O{default}}
  {\use:c{x#1}}
  {\use:c{endx#1}}
\ExplSyntaxOff

因为xparse的环境即使在最后的代码中也可以访问参数。

答案2

让我们看看在中\pushQED\popQED是如何定义的amsthm.sty

    274 \DeclareRobustCommand{\qed}{%
    275   \ifmmode \mathqed
    276   \else
    277     \leavevmode\unskip\penalty9999 \hbox{}\nobreak\hfill
    278     \quad\hbox{\qedsymbol}%
    279   \fi
    280 }
    281 \let\QED@stack\@empty
    282 \let\qed@elt\relax
    283 \newcommand{\pushQED}[1]{%
    284   \toks@{\qed@elt{#1}}\@temptokena\expandafter{\QED@stack}%
    285   \xdef\QED@stack{\the\toks@\the\@temptokena}%
    286 }
    287 \newcommand{\popQED}{%
    288   \begingroup\let\qed@elt\popQED@elt \QED@stack\relax\relax\endgroup
    289 }
    290 \def\popQED@elt#1#2\relax{#1\gdef\QED@stack{#2}}
    291 \newcommand{\qedhere}{%
    292   \begingroup \let\mathqed\math@qedhere
    293     \let\qed@elt\setQED@elt \QED@stack\relax\relax \endgroup
    294 }

名称清楚地表明这与证明中的 QED 标记的排版有关,因此我其中包括了 的定义\qed

有一个“堆栈”,实际上是一个宏,称为\QED@stack,它被初始化为空。

假设我们\pushQED{foo}在堆栈为空时调用。设置了两个临时标记寄存器:首先\toks@设置为\qed@elt{foo},然后\@temptokena设置为包含当前的第一级扩展\QED@stack(在本例中为空)。接下来,\QED@stack重新定义为包含两个寄存器中的标记列表;使用 e-TeX 扩展,这可以通过一条指令实现

\xdef\QED@stack{\unexpanded{\qed@elt{#1}}\unexpanded\expandafter{\QED@stack}}

因此现在\QED@stack将展开为\qed@elt{foo}。如果\pushQED{bar}接下来再展开一个,展开式将变成\qed@elt{bar}\qed@elt{foo}。但我们先讨论简单的情况。

调用时会发生什么\popQED?执行第 288 行的指令,即

\begingroup\let\qed@elt\popQED@elt \QED@stack\relax\relax\endgroup

该宏\qed@elt(通常是\relax,见第 282 行)设置为\popQED@elt 在组内然后\QED@stack\relax\relax进行检查。在你的情况下是

\qed@elt{foo}\relax\relax

由于宏\qed@elt已被重新定义,因此这与 相同\popQED@elt{foo}\relax\relax,并且根据 的定义\popQED@elt

#1 <- {foo}
#2 <- 

因此

foo\gdef\QED@stack{}\relax

会保留在主标记列表中(括号被 TeX 规则删除)。如果是\QED@stack\qed@elt{bar}\qed@elt{foo}我们会得到

#1 <- {bar}
#2 <- \qed@elt{foo}

并将bar\gdef\QED@stack{\qed@elt{foo}}\relax被推入主输入流。

仅仅是因为 的扩展\popQED以 开头\begingroup这一事实就使其在 内部不合法\csname...\endcsname;此外,在该上下文中无法执行分配,因此这从一开始就是一场失败的战斗。

双精度浮点数是为了防止在调用\relax时堆栈为空,也就是没有匹配的命令。\popQED\pushQED

该系统的主要用途是什么? 中的标准proof环境amsthm.sty定义为

    432 \newenvironment{proof}[1][\proofname]{\par
    433   \pushQED{\qed}%
    434   \normalfont \topsep6\p@\@plus6\p@\relax
    435   \trivlist
    436   \item[\hskip\labelsep
    437         \itshape
    438     #1\@addpunct{.}]\ignorespaces
    439 }{%
    440   \popQED\endtrivlist\@endpefalse
    441 }

这个想法是,下属证明环境可能会定义自己的墓碑符号并将其推入堆栈,因此在最终环境中会使用正确的符号。


你能使用堆栈来实现这个目的吗?是的。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\newstack}{m}
 {
  \seq_new:c { g_thorsten_#1_stack_seq }
 }

\NewDocumentCommand{\push}{mm}
 {% #1 is the stack's name, #2 the item to push
  \seq_gpush:cn { g_thorsten_#1_stack_seq } { #2 }
 }

\NewDocumentCommand{\pop}{mo}
 {% #1 is the stack's name, #2 what you should do with the top item
  % reinitialize, in case it has been modified
  \cs_set_eq:NN \__thorsten_stack_exec:n \__thorsten_stack_exec_default:n
  \IfValueT{ #2 }
   {
    \cs_set:Nn \__thorsten_stack_exec:n { #2 }
   }
  \seq_gpop:cNTF { g_thorsten_#1_stack_seq } \l__thorsten_stack_item_tl
   {% if the stack is not empty
    \__thorsten_stack_exec:V \l__thorsten_stack_item_tl
   }
   {% if the stack is empty, issue an error
    \__thorsten_stack_exec:n { \STACKEMPTYERROR }
   }
 }

\tl_new:N \l__thorsten_stack_item_tl
\cs_new_protected:Nn \__thorsten_stack_exec_default:n { #1 }
\cs_set_eq:NN \__thorsten_stack_exec:n \__thorsten_stack_exec_default:n
\cs_generate_variant:Nn \__thorsten_stack_exec:n { V }

\ExplSyntaxOff

\newstack{env}

\newenvironment{myEnv}[1][default]
 {%
  \push{env}{#1}%
  \csname x#1\endcsname
 }
 {%
  \pop{env}[\csname endx##1\endcsname]%
 }

\newenvironment{xdefault}{\par start}{finish\par}
\newenvironment{xinner}{\par startinner}{finishinner\par}

\begin{document}

\begin{myEnv}
\begin{myEnv}[inner]
\end{myEnv}
\end{myEnv}

\newstack{foo}

\push{foo}{A}
\push{foo}{B}
\push{foo}{C}

\pop{foo}[@@#1@@]
\pop{foo}[??#1??]
\pop{foo}[!!#1!!]
\pop{foo}[---#1---]

\end{document}

\push命令将堆栈的名称和要推送的项目作为参数。\pop将堆栈的名称作为强制参数,可选参数是从堆栈顶部移除项目后对弹出的项目执行的操作的模板(默认为仅传递它)。

请注意输入中的空格:\csname x#1 \endcsname与 不同\csname x#1\endcsname

在此处输入图片描述

由于在上一个示例中,该\pop操作是在空堆栈上调用的,因此会产生错误

! Undefined control sequence.
<argument> \STACKEMPTYERROR 

l.69 \pop{foo}[---#1---]

相关内容