我一直在寻找重复项,但似乎找不到,所以这里什么都没有。我正在尝试创建一个通用环境,它可以接收环境名称作为参数并生成该特定环境。如果你想知道为什么要做一些显然没用的事情,原因是我需要重新定义很多环境,所以我能想到的最快的方法是从通用模板开始。事实证明,这本身就是一个挑战:
\documentclass{article}
\newenvironment{mynewenv}[1]
{
{\begin{#1}}
{\end{#1}}
}
\begin{document}
\begin{mynewenv}{math}
1+1
\end{mynewenv}
\end{document}
此 MWE 生成 2 个错误:
- 缺失 } 插入
- 额外的 },或者忘记了 $
这对于调试根本没用...我做错了什么?
答案1
两个问题:正确使用\newenvironment
is
\newenvironment{mynewenv}[<args>][<opt>]{<begdef>}{<enddef>}
并且#1
不能在中使用<enddef>
。
这可行,但在其他情况下可能会失败(参见@Rmanos 的评论):
\documentclass{article}
\newenvironment{mynewenv}[1]
{\def\envname{#1}\begin{#1}}
{\expandafter\end\expandafter{\envname}}
\begin{document}
\begin{mynewenv}{math}
1+1
\end{mynewenv}
\end{document}
答案2
您想要保存当前环境名称。
\documentclass{article}
\makeatletter
\newenvironment{mynewenv}[1]
{\def\mynewenv@currenvir{#1}\csname\mynewenv@currenvir\endcsname}
{\csname end\mynewenv@currenvir\endcsname}
\makeatother
\begin{document}
\begin{mynewenv}{math}
1+1
\end{mynewenv}
\begin{mynewenv}{enumerate}
\item a
\item b
\begin{mynewenv}{itemize}
\item c
\item d
\end{mynewenv}
\item e
\end{mynewenv}
\end{document}
但无论如何,这是一个坏主意。
答案3
我把这个问题当作使用类别代码和宏扩展的练习。
但说实话,我觉得写这些没什么意义
\begin{mynewenv}{EnvironmentWhichIActuallyWant}<Arguments>
而不是直接写
\begin{EnvironmentWhichIActuallyWant}<Arguments>
。
是因为它可能。
您尝试让环境“mynewenv”执行另一个名为“math”的环境。
这样做的问题是:
“mynewenv” 启动“math”。然后在 .tex 输入中\end{...}
遇到“mynewenv”的命令,该命令又执行\end{...}
“math”的命令。
这样,“math”和“mynewenv”的结束命令的顺序,以及这些环境的底层结束命令和这些环境末尾的钩子的执行顺序,相对于这些事情应该执行的顺序是相反的。
\begin
通过修补- 和- 命令可以使这一点变得明显,\end
以便这些命令还可以提供有关当前执行哪个环境的消息:
以下示例按以下顺序传递消息
BEGINNING ENVIRONMENT: document
BEGINNING ENVIRONMENT: mynewenv
BEGINNING ENVIRONMENT: math
ENDING ENVIRONMENT: mynewenv
ENDING ENVIRONMENT: math
ENDING ENVIRONMENT: document
但信息应该按顺序传递
BEGINNING ENVIRONMENT: document
BEGINNING ENVIRONMENT: mynewenv
BEGINNING ENVIRONMENT: math
ENDING ENVIRONMENT: math
ENDING ENVIRONMENT: mynewenv
ENDING ENVIRONMENT: document
\documentclass{article}
\makeatletter
\newenvironment{mynewenv}[1]{\begin{#1}}%
{\expandafter\end\expandafter{\@currenvir}}%
%%
\expandafter\def\csname begin \endcsname#1{%
\message{^^JBEGINNING ENVIRONMENT: \detokenize{#1}}%
\UseHook{env/#1/before}%
\@ifundefined{#1}%
{\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
{\def\reserved@a{\def\@currenvir{#1}\edef\@currenvline{\on@line}%
\@execute@begin@hook{#1}\csname #1\endcsname}}%
\@ignorefalse
\begingroup
\@endpefalse
\reserved@a
}%
%%
\expandafter\def\csname end \endcsname#1{%
\message{^^JENDING ENVIRONMENT: \detokenize{#1}}%
\romannumeral
\IfHookEmptyTF{env/#1/end}%
{\expandafter\z@}%
{\z@\UseHook{env/#1/end}}%
\csname end#1\endcsname
\@checkend{#1}%
\expandafter\endgroup\if@endpe\@doendpe\fi
\UseHook{env/#1/after}%
\if@ignore \@ignorefalse\ignorespaces\fi
}%
\makeatother
\begin{document}
\begin{mynewenv}{math}
1+1
\end{mynewenv}
\end{document}
您可以做的是让“mynewenv”逐字收集所有内容,直到到达\end{mynewenv}
并通过\scantokens
吐出
\end{mynewnv}\begin{#1}<collected stuff>\end{#1}
或
\begin{#1}<collected stuff>\end{#1}\end{mynewnv}
。
所以前段时间我写了自己的界面
\UD@GatherCodeSnippetLoop{<non-special characters gathered so far>}%
{<subset of name of environment gathered so far>}%
{<tokens to apply to stuff gathered>}%
<non-special characters>\end{<name of environment>}
它会切换到 verbatim-catcode-régime 并收集 catcode-12-régime 中的所有内容,直到遇到\end{<name of environment>}
,然后切换回正常的 catcode-régime,将收集的标记作为括号嵌套的未限定参数传递到后面<tokens to apply to stuff gathered>
并执行\end{<name of environment>}
。
您可以按\newenvironment
如下方式使用它:
\newenvironment{foobar}[<args>][<opt-default>]{%
<stuff using parameters according to <args> >
\UD@GatherCodeSnippetLoop{<non-special characters gathered so far>}%
{<subset of name of environment gathered so far>}%
{<tokens to apply to stuff gathered>}%
}{%
<code at end of environment>
}%
环境提供了类似
<tokens to apply to stuff gathered>{<stuff gathered in catcode-12-régime>}
<tokens to apply to stuff gathered>
可以包含用于添加和添加更多内容以及将内容传递给的指令\scantokens
。这可能很棘手,因为\scantokens
您需要确保正确地重新标记内容。
<tokens to apply to stuff gathered>
还必须触发执行,因为.tex 输入文件中出现的\end{foobar}
序列被作为标记所吞没,该标记表示在逐字类别代码制度中结束收集材料的位置。\end{foobar}
\UD@GatherCodeSnippetLoop
使用的环境\UD@GatherCodeSnippetLoop
存在一些缺点:
- 它们不能嵌套!
- 它们不能被
\the
令牌寄存器的宏/扩展调用。
\makeatletter
%%///////////// Code for interface \UD@GatherCodeSnippetLoop and ///////////////
%%///////////// and all suplementary things ////////////////////////////////////
%%===============================================================================
%% End \romannumeral-driven expansion safely:
%%===============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%===============================================================================
%% Check whether argument is empty:
%%===============================================================================
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
\expandafter\UD@stopromannumeral\@firstoftwo}%
}%
\newcommand\UD@exchange[2]{#2#1}%
%%===============================================================================
%% Loop for replacing catcode-10-spaces by catcode-12-spaces
%%===============================================================================
\begingroup
\def\UD@ReplaceSpace#1{%
\endgroup
\@ifdefinable\UD@ReplaceSpace{\long\def\UD@ReplaceSpace##1 {##1#1}}%
}%
\catcode`\ =12\relax
\UD@ReplaceSpace{ }%
\@ifdefinable\UD@GobbleToSpace{\long\def\UD@GobbleToSpace#1 {}}%
\newcommand\UD@SpaceReplaceLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToSpace#1 }{%
\UD@stopromannumeral#1%
}{%
\expandafter\UD@SpaceReplaceLoop\expandafter{\UD@ReplaceSpace#1}%
}%
}%
%%===============================================================================
%% Check if string of non-special character-tokens is substring
%% of another string of non-special character-tokens:
%%===============================================================================
%% \UD@checkstringsubsetof{<possible substring>}{<string>}%
%% {<tokens if <possible substring> and <string> are equal>}%
%% {<tokens if <possible substring> is strict subset of <string>>}%
%% {<<tokens if <possible substring> is not subset of <string>>}%
%% The length of <possible substring> must not exceed the length of <string>!!!
\newcommand\UD@checkstringsubsetof[5]{%
\UD@checkstringsubsetofloop#1\relax#2\relax{{#3}{#4}}{#5}%
}%
\@ifdefinable\UD@checkstringsubsetofloop{%
\def\UD@checkstringsubsetofloop#1#2\relax#3#4\relax{%
\if\string#1\string#3\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\ifx\relax#2\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\ifx\relax#4\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\@firstoftwo\@firstoftwo}{\expandafter\@secondoftwo\@firstoftwo}%
}%
{\UD@checkstringsubsetofloop#2\relax#4\relax}%
}{\expandafter\@secondoftwo}%
}%
}%
%%===============================================================================
%% Gather non-special character-tokens until encountering
%% \end{<name of environment>} :
%%===============================================================================
%% \UD@GatherCodeSnippetLoop{<non-special characters gathered so far>}%
%% {<subset of name of environment gathered so far>}%
%% {<tokens to apply to stuff gathered>}%
%% <non-special characters>\end{<name of environment>}
%%
%% Character-wise gathers <non-special characters>, replaces any non-space by its
%% \catcode-12-pendant. (Space is handled by giving it catcode 12 before applying
%% the routine.)
%% Then does
%% <tokens to apply to stuff gathered>{<non-special characters gathered so far>}
\newcommand\UD@GatherCodeSnippetLoop{%
\begingroup
\let\@tempa\@currenvir
\@onelevel@sanitize\@tempa
\expandafter\endgroup
\expandafter\UD@@GatherCodeSnippetLoop
\expandafter{\romannumeral\expandafter\UD@SpaceReplaceLoop\expandafter{\@tempa}}%
}%
\newcommand\UD@@GatherCodeSnippetLoop[1]{%
\begingroup
\let\do\@makeother % <- this and the next line switch to
\dospecials % verbatim-category-code-régime.
\do\^^I%
\do\^^M%
\begingroup
\escapechar=-1\relax
\edef\@tempa{\string\\end\string{#1\string}}%
\expandafter\endgroup
\expandafter\UD@@@GatherCodeSnippetLoop
\expandafter{\@tempa}{#1}%
}%
\newcommand\UD@@@GatherCodeSnippetLoop[6]{%
\UD@checkstringsubsetof{#4#6}{#1}{%
\endgroup#5{#3}%
}{%
\expandafter\UD@exchange\expandafter{\expandafter{%
\romannumeral
\expandafter\UD@exchange\expandafter{%
\romannumeral\UD@StringifyIfNotSpace{#6}%
}{\UD@stopromannumeral#4}%
}}{\UD@@@GatherCodeSnippetLoop{#1}{#2}{#3}}{#5}%
}{%
\UD@checkstringsubsetof{#6}{#1}{%
#5{#3#4}%
}{%
\expandafter\UD@exchange\expandafter{\expandafter{%
\romannumeral\UD@StringifyIfNotSpace{#6}%
}}{\UD@@@GatherCodeSnippetLoop{#1}{#2}{#3#4}}{#5}%
}{%
\expandafter\UD@exchange\expandafter{\expandafter{%
\romannumeral
\expandafter\UD@exchange\expandafter{%
\romannumeral\UD@StringifyIfNotSpace{#6}%
}{\UD@stopromannumeral#3#4}%
}}{\UD@@@GatherCodeSnippetLoop{#1}{#2}}{}{#5}%
}%
}%
}%
\newcommand\UD@StringifyIfNotSpace[1]{%
\if\string#1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@stopromannumeral}{\expandafter\UD@stopromannumeral\string}#1%
}%
%%///////////// End of code for interface \UD@GatherCodeSnippetLoop /////////////
\makeatother
\makeatletter
%%///////////// Code for environment mynewenv ///////////////////////////////////
\newenvironment{mynewenv}[1]{%
\UD@GatherCodeSnippetLoop{}{}{\wrapmynewenv{#1}}%
}{}%
\begingroup
\def\wrapmynewenv#1#2#3{%
\endgroup
\newcommand\wrapmynewenv[2]{%
\begingroup
\newlinechar=\endlinechar
\scantokens\expandafter{\string\endgroup#1{##1}##2#2{##1}#3}%
\end{mynewenv}%
}%
}%
\catcode`\%=12\relax
\expandafter\expandafter\expandafter\wrapmynewenv
\expandafter\expandafter\expandafter{\expandafter
\string\expandafter\begin\expandafter}\expandafter
{\string\end}{%}%
%%///////////// End of code for environment mynewenv /////////////////////////////
\makeatother
\documentclass{article}
%\makeatletter
%\expandafter\def\csname begin \endcsname#1{%
% \message{^^JBEGINNING ENVIRONMENT: \detokenize{#1}}%
% \UseHook{env/#1/before}%
% \@ifundefined{#1}%
% {\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
% {\def\reserved@a{\def\@currenvir{#1}\edef\@currenvline{\on@line}%
% \@execute@begin@hook{#1}\csname #1\endcsname}}%
% \@ignorefalse
% \begingroup
% \@endpefalse
% \reserved@a
%}%
%%
%\expandafter\def\csname end \endcsname#1{%
% \message{^^JENDING ENVIRONMENT: \detokenize{#1}}%
% \romannumeral
% \IfHookEmptyTF{env/#1/end}%
% {\expandafter\z@}%
% {\z@\UseHook{env/#1/end}}%
% \csname end#1\endcsname
% \@checkend{#1}%
% \expandafter\endgroup\if@endpe\@doendpe\fi
% \UseHook{env/#1/after}%
% \if@ignore \@ignorefalse\ignorespaces\fi
%}%
%\makeatother
\begin{document}
\noindent
\begin{mynewenv}{math}
1+1
\end{mynewenv}
\begin{mynewenv}{verbatim*}
#1 $_ {^ blebb
\end{mynewenv}
\end{document}