我使用此代码(示例)定义了一个新环境:
\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---]