我想编写一个 LaTeX 宏,其代码依赖于父环境,例如:
\newcommand\test{
%if current_environment=env1
test1
%elseif current_environment=env2
test2
%else
}
\begin{env1}
\test %output test1
\end{env1}
\begin{env2}
\test %output test2
\end{env2}
\test %output nothing
任何想法?
答案1
环境的当前名称保存在宏中\@currenvir
。
下面的例子中定义了一个名为 的新测试\ifenv
。该命令的语法是:
\ifenv{environment name}{TRUE}{FALSE}
\ifenv
测试当前环境的名称是否等于输入。根据结果,将使用真或假部分。
\documentclass{article}
\makeatletter
\def\ifenv#1{
\def\@tempa{#1}%
\ifx\@tempa\@currenvir
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\makeatother
\newenvironment{enva}{}{}
\newenvironment{envb}{}{}
\begin{document}
\begin{enva}
\ifenv{enva}{TRUE}{FALSE}
\ifenv{envb}{TRUE}{FALSE}
\end{enva}
\begin{envb}
\ifenv{enva}{TRUE}{FALSE}
\ifenv{envb}{TRUE}{FALSE}
\end{envb}
\end{document}
使用\begin
---\end
来定义很重要\@currenvir
。
正如 Marc van Dongen 提到的,如果合并环境,测试就会失败。
在这种情况下,我会使用另一种解决方案。
\documentclass{article}
\makeatletter
\def\Mycurrentvir{document}
\def\ifenv#1{
\def\@tempa{#1}%
\ifx\@tempa\Mycurrentvir
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\makeatother
\newenvironment{enva}{\def\Mycurrentvir{enva}}{}
\newenvironment{envb}{\def\Mycurrentvir{envb}}{}
\begin{document}
\begin{enva}
\ifenv{enva}{TRUE}{FALSE}
\ifenv{envb}{TRUE}{FALSE}
\end{enva}
\begin{envb}
\ifenv{enva}{TRUE}{FALSE}
\ifenv{envb}{TRUE}{FALSE}
\end{envb}
\end{document}
答案2
根本不需要使用任何条件:让 TeX 为您进行切换!它的工作原理如下:
对于每个需要特殊效果的环境,请调用
\NewEnvCode{<environment>}{<code>}
。您至少应该写\NewEnvCode{document}{<code>}
;这是作为未指定环境(包括顶级;即在document
“环境”内)的默认操作安装的。可以在任何地方调用
\RunEnvCode
。如果是在您的某个特殊环境中,则将运行您的代码;否则,将运行默认代码。您可以通过调用将当前环境设为所有子环境的默认环境\MakeDefault
(实际上,这就是安装的方式document
)。离开调用环境后,该环境将被重置。
以下是我的代码和示例文档:
\documentclass{article}
\usepackage{filecontents}
% Simulated package file
\begin{filecontents}{envcode.sty}
\newcommand\NewEnvCode[2]{%
\expandafter\def\csname code@#1\endcsname{#2}%
\expandafter\def\csname change@code@#1\endcsname{%
\expandafter\let\expandafter\Code\csname code@#1\endcsname
}%
}
\newcommand\MakeDefault{%
\expandafter\let\expandafter\code@@default\csname code@\@currenvir\endcsname
}
\newcommand\RunEnvCode{%
\let\Code=\code@@default
\csname change@code@\@currenvir\endcsname
\Code
}
\AtBeginDocument{\MakeDefault}
\end{filecontents}
\usepackage{envcode}
\NewEnvCode{document}{default code}
\NewEnvCode{equation}{\int_{-\infty}^\infty e^{-x^2/2} dx = \sqrt{2\pi}}
\NewEnvCode{itemize}{itemize!}
\begin{document}
\RunEnvCode
\begin{equation}
\RunEnvCode
\end{equation}
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\begin{itemize}
\item \RunEnvCode
\item
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\item \MakeDefault
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\end{itemize}
\end{document}
好吧,它的实际工作原理如下:
\NewEnvCode
创建一个宏\change@code@#1
,其中#1
是环境的名称,当调用它时,它会重新定义一些调用\Code
来生成代码#2
。实际上,它会创建一个宏,然后\code@#1
生成并执行此宏。#2
\change@code@#1
\let
\Code
\RunEnvCode
首先给出\Code
默认效果\code@@default
,然后尝试使用其他答案中所述的(当前环境的名称)调用其中一个\change@code@<env>
宏。它使用构造此宏名称。<env> = \@currenvir
\csname...\endcsname
由于这种方式的
\csname
工作方式,如果它产生的控制序列尚未定义,它的行为就像\relax
;即什么也不做。因此,如果当前环境不是您指定的环境之一,则\Code
保持定义为默认操作,否则,进行切换。\MakeDefault
只是\let
默认操作\code@@default
,\code@<env>
因此如果\RunEnvCode
在未知环境中调用,它想它正在调用默认值,但该默认值将是最后设置的默认值\MakeDefault
。
如果要添加更多案例,只需调用\NewEnvCode
相关环境即可。
答案3
可以通过以下方式获得更容易定制的版本expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\makeatletter
\NewDocumentCommand{\test}{}
{
\prg_case_str:onn { \@currenvir }
{
{envA}{\testenvA}
{envB}{\testenvB}
}
{\testDefault}
}
\makeatother
\ExplSyntaxOff
\newenvironment{envA}{}{}
\newenvironment{envB}{}{}
\newcommand{\testenvA}{We're~in~\texttt{envA}}
\newcommand{\testenvB}{We're~in~\texttt{envB}}
\newcommand{\testDefault}{We're~neither~in~\texttt{envA}~nor~in~\texttt{envB}}
\begin{document}
\test
\begin{envA}\test\end{envA}
\begin{envB}\test\end{envB}
\end{document}
当然,这些定义没有考虑嵌套环境:所以
\begin{envA}\begin{envC}\test\end{envC}\end{envA}
将生产\testDefault
并不是 \testenvA
。
另一种方法是向环境中添加代码:
\usepackage{etoolbox}
\newcommand{\testenvA}{We're~in~\texttt{envA}}
\newcommand{\testenvB}{We're~in~\texttt{envB}}
\newcommand{\testDefault}{We're~neither~in~\texttt{envA}~nor~in~\texttt{envB}}
\AtBeginEnvironment{envA}{\renewcommand{\test}{\testenvA}}
\AtBeginEnvironment{envB}{\renewcommand{\test}{\testenvB}}
\newcommand{\test}{\testDefault} % default
在这种情况下
\begin{envA}\begin{envC}\test\end{envC}\end{envA}
将会产生\testenvA
。
答案4
因为我相信你应该用于pgfkeys
一切(与编程相关)让我提交第二个答案,其一般行为与第一个答案相同。接口中唯一的区别是\NewEnvCode
只接受一个参数,即以逗号分隔的键值对列表<env> = <code>
(<code>
如果有逗号或等号,则放在括号中)。编码的差异显而易见:
\documentclass{article}
\usepackage{filecontents}
% Simulated package file
\begin{filecontents}{pgfkeys-envcode.sty}
\RequirePackage{pgfkeys}
\pgfkeys{
/envcode/.is family, /envcode,
define/.is family,
code/.is family,
}
\pgfkeys{
/envcode/define,
.unknown/.style = {
/envcode/code/\pgfkeyscurrentname/.code={#1},
},
}
\AtBeginDocument{\MakeDefault}
\newcommand\NewEnvCode[1]{%
\pgfkeys{/envcode/define,#1}%
}
\newcommand\MakeDefault{%
\pgfkeys{/envcode/code/.unknown/.style/.expanded=\@currenvir}%
}
\newcommand\RunEnvCode{%
\pgfkeys{/envcode/code,\@currenvir}%
}
\end{filecontents}
\usepackage{pgfkeys-envcode}
\NewEnvCode{
document = default code,
equation = {\int_{-\infty}^\infty e^{-x^2/2} dx = \sqrt{2\pi}},
itemize = itemize!,
}
\begin{document}
\RunEnvCode
\begin{equation}
\RunEnvCode
\end{equation}
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\begin{itemize}
\item \RunEnvCode
\item
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\item \MakeDefault
\begin{enumerate}
\item \RunEnvCode
\end{enumerate}
\end{itemize}
\end{document}
工作原理如下:
回想一下,它
pgfkeys
有一个键的“目录树”的概念,并且它的键除了存储值之外还可以执行相当通用的功能:例如,它们可以调用其他键,并且有一种通过默认搜索路径处理未知键的机制。这是我利用来获得“默认”行为的机制。<env> = <code>
您传递给的任何键都会\NewEnvCode
在目录中定义一个/envcode/code
名为的键<env>
,并将其设置为<code>
在运行时执行;当您调用时\RunEnvCode
,它会尝试使用来执行此键<env> = \@currenvir
。当
/envcode/code/<env>
不存在时,/envcode/code/.unknown
将改为调用默认的“处理程序”。这是由设置的\MakeDefault
,它表示它应该具有“样式”\@currenvir
(在“编译”时完全展开),这意味着它只调用该名称的键,例如“document”。我将其设置为\MakeDefault
在之后立即调用\begin{document}
,这样在没有其他更改的情况下,默认代码是“document”,您应该设置它。同样的机制也负责
\NewEnvCode
其自身的运作:你给它的每个键都在目录下执行/envcode/define
,绝不在其中定义了任何键。那里的未知处理程序始终运行,我们要求它在 中设置一个/envcode/code
同名的键,其“代码”为<code>
。这为用户提供了便利,因此您不必/.code
在所有内容之后写入。
正如您所看到的,您正在寻找的行为基本上等同于的查找行为pgfkeys
,因此这是一个非常合适的工具,而且在编程方面比 TeX 中发生的有趣事情更出色。显然,这是一个很多幕后更加复杂;这些宏中的每一个可能都会扩展到数十个内部宏,然后才能最终有效地完成我在另一个答案中写到的工作。但是,此代码是自文档化的,并且由于pgfkeys
非常灵活,因此更容易扩展。