用自身链来界定宏

用自身链来界定宏

阅读这个问题@Werner 的回答让我思考是否有可能拥有自定界宏链,例如,\XX abc \XX def \XX xyz或者说

\begin{someenv}
  \XX abc
  \XX def
  \XX xyz
\end{someenv}

最初的定义显然会在这里失败——数量不足\XX。因此,重新定义的第一个步骤\XX可能是

\def\XX#1\XX{do sth with #1\\ \XX}

现在的问题是如何优雅地结束这个链条。一个相当粗鲁和不是理想的方式如下面的MWE所示。

\documentclass{article}

\def\XX#1\XX{do sth with #1\\ \XX}

\newenvironment{someenv}{\center}{\endcenter}

\def\abc{def}

\begin{document}
\begin{someenv}
  \XX xyz
  \XX abc
  \XX \abc
  \let\XX\empty %this is the bad guy that needs to be busted
\end{someenv}
\end{document}

答案1

除非您获取所需的令牌列表作为参数,否则您无法真正做到这一点。

这是一种使用的可能性environ;使用expl3不是必需的,但它为以后完成所需的工作提供了一个很好的框架。

\documentclass{article}
\usepackage{environ,expl3}

\ExplSyntaxOn
\NewEnviron{collectitems}
 {
  % clear the sequence
  \seq_clear:N \l_ruben_collected_items_seq
  % process the body
  \exp_last_unbraced:No \ruben_collect_item:nw \BODY \item \q_stop
  % do something with the sequence
  \seq_show:N \l_ruben_collected_items_seq
 }
\seq_new:N \l_ruben_collected_items_seq
\cs_new_protected:Npn \ruben_collect_item:nw #1 \item #2 \q_stop
 {
  \seq_put_right:Nx \l_ruben_collected_items_seq { \tl_trim_spaces:n { #1 } }
  \tl_if_empty:nF { #2 }
   {
    \ruben_collect_item:nw #2 \q_stop
   }
 }
\ExplSyntaxOff

\begin{document}

\begin{collectitems}
\item Apple
\item Banana
\item Cherry
\end{collectitems}

\begin{collectitems}
\end{collectitems}

\end{document}

主体收集在 中,在添加和\BODY之前进行扩展。该函数在每个 时执行,直到后紧接着。项目按顺序排列;序列中的第一个项目将包含第一个标记之前的内容,可用于检查标记是否真正可见,如示例所示。\ruben_collect_item:nw\item \q_stop\ruben_collect_item:nw\item\item\q_stop\item\item

终端上的输出是

The sequence \l_ruben_collected_items_seq contains the items (without outer braces):
>  {}
>  {Apple}
>  {Banana}
>  {Cherry}.
<recently read> }

l.31 \end{collectitems}

? 
The sequence \l_ruben_collected_items_seq contains the items (without outer braces):
>  {}.
<recently read> }

l.34 \end{collectitems}

当然,您可以利用该序列做一些更有用的事情。


将描述环境的左边距“翻译”为经典命令,使左边距适应最宽标签的宽度。

\documentclass{article}
\usepackage{environ,enumitem}

\newlength\rubenwidth
\NewEnviron{xdescription}{%
  \expandafter\rubenprocess\BODY\item\rubenprocess
  \begin{itemize}[labelwidth=\rubenwidth,leftmargin=\rubenwidth,align=left,font=\bfseries]
  \BODY
  \end{itemize}
}
\makeatletter
\def\rubenprocess#1#2\item#3\rubenprocess{% #1 is always \item
  \ruben@item#2\ruben@item
  \if\relax\detokenize{#3}\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {\rubenprocess\item#3\rubenprocess}%
}
\def\ruben@item[#1]#2\ruben@item{%
  \settowidth\@tempdima{\bfseries#1}%
  \ifdim\rubenwidth<\@tempdima\rubenwidth=\@tempdima\fi
}

\begin{document}

\begin{xdescription}
\item[Apple] a round fruit
\item[Banana] not so round fruit
\item[Strawberry] small fruit with some more words
  to get the text into two lines; will we succeed?
\end{xdescription}

\end{document}

在此处输入图片描述

该想法有多种可能的变化。

答案2

这就是你想要的东西吗?

\documentclass{article}
\newenvironment{someenv}{\par\centering}{\par}
\newenvironment{XX}{\endXX\bfseries\itshape Do sth with\ }{\mdseries\upshape\par}
\let\notXX\endXX
\def\abc{ABC}
\begin{document}
before
\begin{someenv}
  \XX xyz 
  \XX abc
  \XX \abc 
  \notXX not under \verb|\XX| control % USE \notXX TO FORCE BREAK FROM \XX CONTROL
  \XX ending line
\end{someenv}
after
\end{document}

在此处输入图片描述

你可以发挥想象力,制作出随着环境\XX的迭代而变化的“东西” 。\XX

\documentclass{article}
\newcounter{instance}
\newenvironment{someenv}{\par\centering\setcounter{instance}{0}}{\par}
\newenvironment{XX}{\endXX\bfseries\itshape\stepcounter{instance}
    Iteration \theinstance: Do sth with\ }
  {\mdseries\upshape\par}
\let\notXX\endXX
\def\abc{ABC}
\begin{document}
before
\begin{someenv}
  \XX xyz 
  \XX abc
  \XX \abc 
  \notXX not under \verb|\XX| control % USE \notXX TO FORCE BREAK FROM \XX CONTROL
  \XX ending line
\end{someenv}
after
\end{document}

在此处输入图片描述

答案3

您可以使用以下代码宏参数由多个分隔符分隔。此代码定义\seplist宏。其参数是分隔符列表。您的示例中的分隔符是\XX\end(前面有空格)。的定义\XX如下所示:

\def\XXa#1{... macro with normal #1 parameter\par\sepused}
\def\XX{\seplist{{ \XX}{ \end}}\XXa}

\begin{someenv}
   \XX abc
   \XX def
   \XX xyz
\end{someenv}

相关内容