执行任意环境的环境

执行任意环境的环境

我一直在寻找重复项,但似乎找不到,所以这里什么都没有。我正在尝试创建一个通用环境,它可以接收环境名称作为参数并生成该特定环境。如果你想知道为什么要做一些显然没用的事情,原因是我需要重新定义很多环境,所以我能想到的最快的方法是从通用模板开始。事实证明,这本身就是一个挑战:

\documentclass{article}

\newenvironment{mynewenv}[1]
{
    {\begin{#1}}
    {\end{#1}}
}

\begin{document}
    \begin{mynewenv}{math}
        1+1
    \end{mynewenv}
\end{document}

此 MWE 生成 2 个错误:

  • 缺失 } 插入
  • 额外的 },或者忘记了 $

这对于调试根本没用...我做错了什么?

答案1

两个问题:正确使用\newenvironmentis

\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}

在此处输入图片描述

相关内容