我有一个自定义宏和自定义环境。
\begin{myenv}
...
\mycmd
...
\mycmd
...
\mycmd % last usage
...
\end{myenv}
我想检测 的首次使用和最后一次使用\mycmd
,并打印一些特殊内容。目前,我正在使用\toggletrue
、\togglefalse
和\iftoggle
来检测首次使用。但是,我不知道如何检测最后一次使用。我该怎么做?
.cls
PS 我无法修改该命令的用法,因为我将其作为自动生成文档的一部分。
答案1
编辑以获得更强大的方法
最初的答案存在缺陷,因为它\mycmd
仅限于在环境内部使用,而且如果在括号内使用,myenv
它会产生低级 TeX 错误。\mycmd
下列的这个建议我修改了@BrunoLeFloch 的逻辑:在新方法中,命令\mycmd
根本不与环境绑定myenv
,而是myenv
修改为环境的定义(使用\Collect@Body
来自包环境) 收集其主体(因此,与之前的方法一样,这与使用逐字材料不兼容),并对其进行解析,以便用标记替换最后找到的\mycmd
标记\mycmdLAST
。因此,只需定义即可\mycmdLAST
执行所需的任何操作。
在这种方法中\mycmd
,它不是重新定义的,也可以在环境中的括号内使用而不会出现错误,但无法检测到。在示例中,我定义\mycmdLAST
(按照 Bruno 的评论)进行本地重新定义,\mycmd
因此如果在之后遇到它\mycmdLAST
(必然隐藏在括号中),它会故意引发一些错误消息。但这取决于文档作者决定宏在这些情况下应该做什么。
总结一下:用户只需加载包environ
并将两个标记放在环境定义的“开始”部分的最后\Collect@Body\mycmd@Parse
。环境名称是任意的,但命令名称必须是\mycmd
。该命令\mycmd
可以在文档中的任何地方使用,不需要特殊定义。但必须定义一个\mycmdLAST
命令(先验地具有与原始命令相同数量的参数\mycmd
,但它可以设置一个切换,然后插入标记\mycmd
,后者获取参数并已定义为检查切换),并且如果\mycmd
是环境中的最后一个,则\mycmdLAST
执行。
\documentclass{article}
\usepackage{color}
\usepackage{environ}
\makeatletter
\newtoks\mytoksA
\newtoks\mytoksB
\newenvironment{myenv}{BEGIN CODE OF ENVIRONMENT\par
\Collect@Body\mycmd@Parse}
{\par END CODE OF ENVIRONMENT\par}
\long\def\mycmd@Parse #1{\mytoksA{}%
\mycmd@Parse@start\empty#1\mycmd\mycmd@Parse@start
\the\mytoksA}
\long\def\mycmd@Parse@start #1\mycmd{%
\mytoksB\expandafter{#1}%
\futurelet\mycmd@Token
\mycmd@Parse@checkifnone
}%
\def\mycmd@Parse@checkifnone {%
\ifx\mycmd@Token\mycmd@Parse@start
\mytoksA\expandafter{\the\mytoksB}% no use of \mycmd in environment
\expandafter\@gobbletwo % get rid of \mycmd@Parse@start end token
\else
\expandafter\mycmd@Parse@loop
\fi\empty
}%
\long\def\mycmd@Parse@loop #1\mycmd{%
\mytoksA\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\mytoksA\the\mytoksB}%
\mytoksB\expandafter{#1}%
\futurelet\mycmd@Token
\mycmd@Parse@check % check if we had hit earlier the last \mycmd
}%
\def\mycmd@Parse@check {%
\ifx\mycmd@Token\mycmd@Parse@start
\mytoksA\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\mytoksA\expandafter
\mycmdLAST
\the\mytoksB}%
\expandafter\@gobbletwo % get rid of \mycmd@Parse@start end token
\else
\mytoksA\expandafter{\the\mytoksA\mycmd}%
\expandafter\mycmd@Parse@loop
\fi\empty
}%
\makeatother
\newcommand\mycmdLAST{% whatever is wanted
% use same number of arguments as \mycmd
\textcolor{blue}{\bfseries I am last!}%
% optional: (use same number of arguments as \mycmd)
\renewcommand{\mycmd}% just an example
{{\color{red}ERROR: USAGE INSIDE BRACES AFTER LAST ONE AT TOP LEVEL}}%
}
\newcommand\mycmd{\textcolor{red}{\bfseries I WAS HERE!}}
\begin{document}
Test 1:
\begin{myenv}\mycmd
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some \emph{\mycmd basic functionality} for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements {\footnotesize\mycmd} the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] \mycmd deals with the computation of continued fractions.
\end{description}
\end{myenv}
\bigskip
Test 2:
\begin{myenv}
No usage of \texttt{\string\mycmd} here.
\end{myenv}
\bigskip
Test 3:
\begin{myenv}
\mycmd
Here we have a braced one after the last usage {\mycmd}
\end{myenv}
\bigskip
Test 4:
\begin{myenv}
Here we have only a braced one {\mycmd}
\end{myenv}
\end{document}
先前的答案
这是一个只需要一次传递并且不需要多次扩展内容的方法。
缺陷:您不能嵌套您的自定义(否则最后的测试将对外部测试myenv
产生误报)。\mycmd
缺陷 2:括号\mycmd
内的括号不会被其他括号检测到,因此可能导致误报。此外,括号\mycmd
内的括号可能会导致 TeX 错误,因为它找不到它\end{myenv}
要查找的内容。
简而言之,该解决方案适用于\mycmd
单一环境内的顶层myenv
,并且它们实际上可能位于子环境内(但不是再次myenv
)。
\documentclass{article}
\newenvironment{myenv}{}{}
\newcommand{\myenvname}{myenv}
\newif\ifmycmdlast
\newtoks\mytoksA
\newtoks\mytoksB
\makeatletter
\newcommand{\mycmd}{\mytoksA{}\mycmd@\empty}%
% \empty token serves to avoid brace removal next
\long\def\mycmd@#1\end#2{%
\mytoksB\expandafter{#1\end{#2}}%
\mytoksA\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\mytoksA\the\mytoksB}%
\long\def\mycmd@temp{#2}%
\ifx\mycmd@temp\myenvname
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
\mycmd@check{\mycmd@\empty}%
}%
\def\mycmd@check
{\expandafter\mycmd@check@i\the\mytoksA\mycmd\mycmd\mycmd@temp}
\long\def\mycmd@check@i #1\mycmd#2\mycmd#3\mycmd@temp{%
\if\relax\detokenize{#3}\relax
\mycmdlasttrue
\else
\mycmdlastfalse
\fi
\expandafter\myRealCmd\the\mytoksA
}
\makeatother
\newcommand\myRealCmd{% whatever is wanted
\ifmycmdlast
\textbf{\textcolor{blue}{I am last}}%
\else
\textbf{\textcolor{red}{I am not last}}%
\fi
}
\usepackage{color}
\begin{document}
\begin{myenv}
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some basic functionality for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] deals with the computation of continued fractions.
\end{description}
\end{myenv}
\end{document}
我实际上想要这个测试:
\begin{document}
\begin{myenv}\mycmd
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some basic functionality for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] deals with the computation of continued fractions.
\end{description}
\mycmd
\end{myenv}
\end{document}
产生
答案2
以下是一个简单的方法,确实有效,但如果myenv
确实包含图形或任何编号的东西,就会导致问题,因为完整内容会被评估两次(一次在一个框内,不会被打印),请注意应该\mycmd@orig
包含正确的宏定义,如\mycmd
在环境中定义的那样。
编辑:我添加了指定在箱体测试后应重置的计数器的可能性。默认情况下,的内容\countlist
被重置,但可以将其他计数器作为可选参数提供给myenv
。
\documentclass[]{article}
\makeatletter
\def\csedef#1#2{\expandafter\edef\csname#1\endcsname{#2}}
\newcount\mycmdcount
\def\countlist{figure,table}
\def\b@ckupcount@myenv{%
\@for\cs:=\countlist\do{%
\csedef{b@ckup@myenv@\cs}{\the\value{\cs}}}}
\def\rest@recount@myenv{%
\@for\cs:=\countlist\do{%
\setcounter{\cs}{\csname b@ckup@myenv@\cs\endcsname}}}
\newenvironment*{myenv}[1][]{%
\if\relax\detokenize{#1}\relax%
\else%
\edef\countlist{\countlist,#1}%
\fi%
\grab@myenv%
}{%
}
\def\mycmd@orig{%
\global\advance\mycmdcount by 1\relax%
\ifnum\mycmdcount=1\relax%
first usage%
\else%
\expandafter\ifnum\lastmycmdcount=\mycmdcount\relax%
last usage%
\else%
a usage, not the first, not the last%
\fi%
\fi%
}
\def\grab@myenv#1\END{%
\mycmdcount=0\relax%
\b@ckupcount@myenv%
\bgroup%
\def\mycmd{\global\advance\mycmdcount by 1\relax}%
\setbox0\vbox{#1}%
\xdef\lastmycmdcount{\the\mycmdcount}%
\egroup%
\rest@recount@myenv%
\let\mycmd\mycmd@orig%
\mycmdcount=0\relax%
#1%
\end}
\makeatother
\begin{document}
\tableofcontents
\begin{myenv}[section]
\section{foo}
foo bar baz\\
\mycmd\\% first
foo bar baz\\
\mycmd\\% another usage
foo bar baz\\
\mycmd\\% last
\END{myenv}
\end{document}
答案3
这假设\mycmd
总是显式出现(未被其他宏调用)。这个想法是搜索\mycmd
直到最后都没有被任何其他自身调用的;正则表达式是
\c{mycmd}([^\c{mycmd]*)\Z
这与描述完全相符。
\documentclass{article}
\usepackage{color}
\usepackage{xparse,environ}
\ExplSyntaxOn
\NewEnviron{myenv}
{
\regex_replace_once:nnN
{ \c{mycmd}([^\c{mycmd}]*)\Z }
{ \c{mycmdLAST}\1 }
\BODY
\BODY
}
\ExplSyntaxOff
\newcommand\mycmdLAST{% whatever is wanted
\textcolor{blue}{\bfseries I am last!}%
}
\newcommand\mycmd{\textcolor{red}{\bfseries I WAS HERE!}}
\begin{document}
Test 1:
\begin{myenv}\mycmd
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some \emph{\mycmd{} basic functionality} for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements {\footnotesize\mycmd} the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] \mycmd deals with the computation of continued fractions.
\end{description}
\end{myenv}
\bigskip
Test 2:
\begin{myenv}
No usage of \texttt{\string\mycmd} here.
\end{myenv}
\bigskip
Test 3:
\begin{myenv}
\mycmd{}
Here we have a braced one after the last usage {\mycmd} with something after
\end{myenv}
\bigskip
Test 4:
\begin{myenv}
Here we have only a braced one {\mycmd}
\end{myenv}
Test 5:
\begin{myenv}
Here we have only a braced one {\mycmd} and something after
\end{myenv}
\end{document}
我使用的测试与 jfbu 的答案基本相同。
您可以扩展它以捕获第一次使用情况;当然,您必须决定在出现一次情况时会发生什么\mycmd
。
\documentclass{article}
\usepackage{color}
\usepackage{xparse,environ}
\ExplSyntaxOn
\NewEnviron{myenv}
{
\regex_replace_once:nnN
{ \c{mycmd} }
{ \c{mycmdFIRST} }
\BODY
\regex_replace_once:nnN
{ \c{mycmd}([^\c{mycmd}]*)\Z }
{ \c{mycmdLAST}\1 }
\BODY
\BODY
}
\ExplSyntaxOff
\newcommand\mycmd{\textcolor{red}{\bfseries I WAS HERE!}}
\newcommand\mycmdFIRST{\textcolor{green}{\bfseries I am first!}}
\newcommand\mycmdLAST{% whatever is wanted
\textcolor{blue}{\bfseries I am last!}%
}
\begin{document}
Test 1:
\begin{myenv}\mycmd
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some \emph{\mycmd{} basic functionality} for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements {\footnotesize\mycmd} the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] \mycmd deals with the computation of continued fractions.
\end{description}
\end{myenv}
\bigskip
Test 2:
\begin{myenv}
No usage of \texttt{\string\mycmd} here.
\end{myenv}
\bigskip
Test 3:
\begin{myenv}
\mycmd{}
Here we have a braced one after the last usage {\mycmd} with something after
\end{myenv}
\bigskip
Test 4:
\begin{myenv}
Here we have only a braced one {\mycmd}
\end{myenv}
Test 5:
\begin{myenv}
Here we have only a braced one {\mycmd} and something after
\end{myenv}
\end{document}
例如,我们可以决定\mycmd
执行一个孤独的外观\mycmdLONELY
:
\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse,environ}
\ExplSyntaxOn
\NewEnviron{myenv}
{
\regex_count:nVN { \c{mycmd} } \BODY \l_tmpa_int
\int_compare:nTF { \l_tmpa_int < 2 }
{ % less than two occurrences
\regex_replace_once:nnN { \c{mycmd} } { \c{mycmdLONELY} } \BODY
}
{
\regex_replace_once:nnN
{ (.*?)\c{mycmd}(.*)\c{mycmd}([^\c{mycmd}]*) }
{ \1 \c{mycmdFIRST} \2 \c{mycmdLAST} \3 }
\BODY
}
% deliver the contents
\BODY
}
\cs_generate_variant:Nn \regex_count:nnN { nV }
\ExplSyntaxOff
\newcommand\mycmd{\textcolor{red}{\bfseries I WAS HERE!}}
\newcommand\mycmdLONELY{\textcolor{green!50!blue}{\bfseries I feel lonely!}}
\newcommand\mycmdFIRST{\textcolor{green}{\bfseries I am first!}}
\newcommand\mycmdLAST{\textcolor{blue}{\bfseries I am last!}}
\begin{document}
Test 1:
\begin{myenv}\mycmd
\begin{description}
\item[xintbinhex] \mycmd
is for conversions to and from binary and
hexadecimal \mycmd bases.
\item[xintseries] provides some \emph{\mycmd{} basic functionality} for computing in an
expandable manner partial sums of series and power series with fractional
coefficients.\mycmd
\item[xintgcd] implements {\footnotesize\mycmd} the Euclidean algorithm and its typesetting.\mycmd
\item[xintcfrac] \mycmd deals with the computation of continued fractions.
\end{description}
\end{myenv}
\bigskip
Test 2:
\begin{myenv}
No usage of \texttt{\char`\\mycmd} here.
\end{myenv}
\bigskip
Test 3:
\begin{myenv}
\mycmd{}
Here we have a braced one after the last usage {\mycmd} with something after
\end{myenv}
\bigskip
Test 4:
\begin{myenv}
Here we have only a braced one {\mycmd}
\end{myenv}
Test 5:
\begin{myenv}
Here we have only a braced one {\mycmd} and something after
\end{myenv}
\end{document}