这个问题导致了一个新的方案的出现:
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}