使用 \BODY ( environment 包) 的嵌套环境有什么问题?

使用 \BODY ( environment 包) 的嵌套环境有什么问题?

这个问题导致了一个新的方案的出现:
newenviron

我想定义一些环境,将其内容放入多个不同的其他环境中。为了访问内容,我使用了environ引入\BODY命令的包。然而,在某些时候,pdflatex 似乎陷入了无限循环。

我将其简化为一个最小的例子:

\documentclass{article}
\usepackage{environ}

\NewEnviron{assertion}{Assertion: \BODY}
\NewEnviron{outerassertion}{\begin{assertion}\BODY\end{assertion}}

\begin{document}

\begin{assertion}
test
\end{assertion}

\begin{outerassertion}
test2
\end{outerassertion}
\end{document}

有什么办法可以解决这个问题吗?我猜问题出在命令上,\BODY因为只要我在其中一个新环境中使用它,一切就都能正常工作。

答案1

\BODY正如你所怀疑的那样,你正在与外部环境发生冲突

  \def\BODY{\BODY}

这使得 TeX 处于循环中,你可以这样做

\documentclass{article}
\usepackage{environ}

\NewEnviron{assertion}{Assertion: \BODY}{}
\NewEnviron{outerassertion}{\let\xBODY\BODY\begin{assertion}\xBODY\end{assertion}}{}

\begin{document}



\begin{assertion}
test
\end{assertion}

\begin{outerassertion}
test2
\end{outerassertion}
\end{document}

尽管在这种情况下,使用标准定义会更简单、更有效\newenvironment,可以完全避免这个问题。

答案2

David 给出了一个简单、常识性的解决方案,但你的问题引出了一个名为newenviron,用户无需采取任何技巧即可使用。\envbody并且\<env name>body可以用于将某些代码应用到环境主体。

\begin{filecontents*}{newenviron.sty}
% Collect environment body in macros \envbody and \<env name>body.
\@ifpackageloaded{catoptions}{}{\RequirePackage{catoptions}[2011/12/12]}
\UseNormalCatcodes
\StyleFilePurpose{Collect and execute environment body (AM)}
\StyleFileRCSInfo
$Id: newenviron.sty,v 1.0 2013/03/08 09:00:00 Ahmed Musa Exp $
\ProvidesPackage{newenviron}[\StyleFileInfo]
\NeedsTeXFormat{LaTeX2e}[2011/06/27]
\cptnewvariables{toks}[nenv@]{temptoks}
\new@def\nenv@gobbletomarker#1\nenv@endmarker{}
\new@def*\nevn@quark{}
\new@def*\AlwaysTrimEnvEntries{\global\nenv@alwaystrimtrue}
\new@def*\nenv@trimspace{%
  \ifdefboolTF{nenv@alwaystrim}\cpttrimspace\unexpanded
}
\new@def*\nenv@everybegin@hook{}
\robust@def*\EveryBeginOfEnvironment#1{%
  \xifinsetTF
  {\detokenize{\nevn@quark#1\nevn@quark}}
  {\cptoxdetok\nenv@everybegin@hook}{}{%
    \edef\nenv@everybegin@hook{%
      \expandcsonce\nenv@everybegin@hook
      \noexpand\nevn@quark\unexpanded{#1}\noexpand\nevn@quark
    }%
  }%
}
\new@def*\nenv@everyend@hook{}
\robust@def*\EveryEndOfEnvironment#1{%
  \xifinsetTF
  {\detokenize{\nevn@quark#1\nevn@quark}}
  {\cptoxdetok\nenv@everyend@hook}{}{%
    \edef\nenv@everyend@hook{%
      \expandcsonce\nenv@everyend@hook
      \noexpand\nevn@quark\unexpanded{#1}\noexpand\nevn@quark
    }%
  }%
}
\EveryEndOfEnvironment{\@ignoretrue}
\robust@def*\nenv@appto#1#2{%
  \ifdefTF#1{%
    \edef#1{\expandcsonce#1\unexpanded{#2}}%
  }{%
    \edef#1{\unexpanded{#2}}%
  }%
}
%
% \newenviron<optional *>
%   {<name>}[<narg>][<default of 1st arg>]{<start-code>}{<end-code>}
%
% \renewenviron<optional *>
%   {<name>}[<narg>][<default of 1st arg>]{<start-code>}{<end-code>}
%
\robust@def*\newenviron{\cpt@starorlong\nenv@newenviron}
\robust@def*\nenv@newenviron#1{%
  \edef\cpt@tempa{\nenv@trimspace{#1}}%
  \cptexpandarg\cpt@testopt
    {\nenv@newenviron@a{\expandcsonce\cpt@tempa}}{0}%
}
\robust@def*\nenv@newenviron@a#1[#2]{%
  \cpt@ifbrack{\nenv@newenviron@b#1[#2]}{\nenv@newenviron@c{#1}{[#2]}}%
}
\robust@def*\nenv@newenviron@b#1[#2][#3]{\nenv@newenviron@c{#1}{[#2][{#3}]}}
\robust@def\nenv@newenviron@c#1#2#3#4{%
  \ifcsndefTF{#1}{}{\letcsntocsn{#1}{end#1}}%
  \aftercsname\new@command{#1}#2{%
    \edef\nenv@beforebody{\nenv@trimspace{#3}}%
    \nenv@everybegin@hook
    \nenv@collectbody
  }%
  \l@ngrel@x\csn@edef{end#1}{%
    \def\noexpand\cpt@prova{\nenv@trimspace{#4}}\noexpand\cpt@prova
    \noexpand\nenv@everyend@hook
  }%
}
\robust@def*\nenv@collectbody{%
  \begingroup
  \toks@{}%
  \everyeof{\end{EOF}\relax}%
  \nenv@collectbody@a
}
\robust@def\nenv@collectbody@a#1\end#2{%
  \nenv@temptoks{%
    \cptexpanded{%
      \toks@{%
        \the\toks@\nenv@trimspace{#1}%
        \noexpand\end{\expandcsonce\cpt@argofend}%
      }%
    }%
    \nenv@collectbody@a
  }%
  \edef\cpt@argofend{\cpttrimspace{#2}}%
  \ifcseqTF\cpt@argofend\@currenvir{%
    \def\cpt@tempa{}%
    \nenv@pushbegin#1\begin\end\nenv@endmarker
    \ifcsemptyTF\cpt@tempa{%
      \cptexpanded{\endgroup
        \csn@edef{\@currenvir body}{%
          \noexpand\unexpanded{\the\toks@\nenv@trimspace{#1}}%
        }%
        \letcstocsn\noexpand\envbody{\@currenvir body}%
        \unexpanded{%
          \nenv@beforebody\relax
          \ifdefboolTF{nenv@alwaystrim}\@ignoretrue\relax
        }%
        \noexpand\end{\cpt@argofend}%
      }%
    }{%
      \the\nenv@temptoks
    }%
  }{%
    \oifstrcmpTF{\cpt@argofend}{document}{%
      \expandafter\endgroup\expandafter
        \@checkend\expandafter{\cpt@argofend}%
    }{%
      \oifstrcmpTF{\cpt@argofend}{EOF}{%
        \expandafter\endgroup\expandafter
          \@checkend\expandafter{\cpt@argofend}%
      }{%
        \the\nenv@temptoks
      }%
    }%
  }%
}
\new@def\nenv@pushbegin#1\begin#2{%
  \expandafter\ifx\cpt@car#2x\car@nil\end
    \expandafter\@gobble
  \else
    \edef\cpt@prova{\cpttrimspace{#2}}%
    \ifx\cpt@prova\cpt@argofend
      \def\cpt@tempa{x}%
      \expandafter\expandafter\expandafter\nenv@gobbletomarker
    \else
      \expandafter\expandafter\expandafter\nenv@pushbegin
    \fi
  \fi
}
\robust@def*\renewenviron{\cpt@starorlong\nenv@renewenviron}
\robust@def*\nenv@renewenviron#1{%
  \edef\cpt@tempa{\nenv@trimspace{#1}}%
  \ifcsndefTF\cpt@tempa
    {}
    {\@latex@error{Environment #1 is undefined}\@ehd}%
  \letcsntocs\cpt@tempa\relax
  \letcsntocs{end\cpt@tempa}\relax
  \expandafter\nenv@newenviron\expandafter{\cpt@tempa}%
}
\XDeclareBooleanOption{alwaystrim}[true](nenv@){}{}
\XDeclareOption*{\@@warning{Unknown option '\CurrentOption' ignored}}
\XExecuteOptions{alwaystrim}
\XProcessOptions*\relax
\endinput
\end{filecontents*}

例子:

\documentclass{article}
\usepackage{newenviron,xcolor}

% \envbody will always work for 'unnested' environments:
%   \newenviron{assertion}{}{Assertion: \envbody}

% But if you intend to nest the environment, it will be safer to use \assertionbody:
\newenviron{assertion}{%
  \def\acmd##1{##1}% test <start-code>.
}{%
  \def\bcmd##1{##1}% test <end-code>.
  Assertion: \assertionbody
}
\newenviron{outerassertion}{%
  % Put any start code here.
}{%
  \textcolor{blue}{Outer assertion:} %
  \begin{assertion}\outerassertionbody\end{assertion}%
}

\begin{document}

% Just for testing:
\EveryEndOfEnvironment{\def\ccmd#1{#1}}

\begin{assertion}
  test
\end{assertion}
\endgraf\bigskip

\begin{outerassertion}
  test2
\end{outerassertion}
\endgraf\bigskip

\begin{assertion}
  Level-1 test.
  \begin{assertion}
  Another level-1 test.
  \end{assertion}
\end{assertion}
\endgraf\bigskip

% Another test. Note the use of \usename{env-1body} and \usename{env-2body}:
\newenviron{env-1}[2][blue]{%
  \fboxrule=#2\relax
  \cptdimdef\temp{.5\textwidth}%
  \endgraf\noindent
  \fcolorbox{#1}{gray!10}{\parbox{\temp}{\textcolor{#1}{\usename{env-1body}}}}%
}{}
\newenviron*{env-2}[1][black]{%
  \noindent
  \fcolorbox{#1}{gray!30}{%
    \parbox{.7\textwidth}{%
      \leftskip=1cm
      \textcolor{#1}{\usename{env-2body}}%
    }%
  }%
}{%
  \def\testcmd##1{##1}% test <end-code>.
}

\begin{env-2}[red]
  Outer box\endgraf
  \def\tempa#1{***#1***}\tempa{aa}%
  \endgraf\vspace*{5mm}%
  \begin{env-1}[blue]{1pt}%
    Inner box\endgraf\vspace*{5mm}%
    \def\tempa#1{+++#1+++}\tempa{bb}%
  \end{env-1}%
  \begin{env-1}[brown]{4pt}%
    Inner box\endgraf\vspace*{5mm}%
    \def\tempa#1{---#1---}\tempa{cc}%
  \end{env-1}%
\end{env-2}

\end{document}

在此处输入图片描述

相关内容