检测自定义环境中自定义命令的最后使用情况

检测自定义环境中自定义命令的最后使用情况

我有一个自定义宏和自定义环境。

\begin{myenv}
...
\mycmd
...
\mycmd
...
\mycmd % last usage
...
\end{myenv}

我想检测 的首次使用和最后一次使用\mycmd,并打印一些特殊内容。目前,我正在使用\toggletrue\togglefalse\iftoggle来检测首次使用。但是,我不知道如何检测最后一次使用。我该怎么做?

.clsPS 我无法修改该命令的用法,因为我将其作为自动生成文档的一部分。

答案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}

在此处输入图片描述

相关内容