用于关闭所有打开的环境,组和参数分隔符的宏

用于关闭所有打开的环境,组和参数分隔符的宏

初始请求

我正在寻找一个可以关闭所有花括号和所有开放环境的宏。理想情况下,我可以编写以下类型的代码:

\Open\begin{minipage}\begin{quote}\texttt{
      \textbf{Something
\Close % will close all open environments and curly brackets opened
       % since the most recent \Open

如果可以创造出这样的怪物,我会将其映射\Open到 Unicode 的粗花括号和\close粗花括号,即❴❵U+2773 和 U+2774。

(是的,我知道有些人认为这是一件令人厌恶的事情,但人们也这样认为 BCPL 中的花括号,尤其是当被迫使用二合字母或三合字母时)

使用此宏,你甚至可以通过以下方式删除更多的 LaTeX 文本“杂乱”

\Open\begin{minipage}\begin{quote}\texttt{
      \textbf{Something
\Close % will close all open environments and curly brackets opened
       % since the most recent \Open

甚至

❴\minipage{\quote{\texttt{
      \textbf{Something
❵      % will close all open environments and curly brackets opened
       % since the most recent ❵

(是的,我意识到这会让一些 LaTeX IDE 抓狂)

通过\Open打开上下文,我们甚至可以写入:

❴\minipage\quote\texttt{
      \textbf{Something

即删除环境的左花括号。对于接受参数的宏,代码必须重写为

❴\minipage\quote\ttfamily\bfseries
   Something

这不仅简洁,而且对 IDE 也更友好。奇怪的是,它也可以与普通花括号一起使用。

如果有人感兴趣的话,这里有另一个用例:而不是输入

\newcommand\kk[1]{\textcolor{RoyalBlue}{\text{\textup{\textbf{\texttt{#1}}}}}}

仔细数一下右括号,我们会得到更容易目视检查的代码:

\newcommand\kk[1]❴\textcolor{RoyalBlue}{\text{\textup{\textbf{\texttt{#1❵ 

分析

根据下面的评论,我了解到这个问题需要最多四个部分的答案:

  • 用于关闭所有待处理环境的宏,该宏由以下项开始:\begin{env}
  • 用于关闭自上次放下锚点以来打开的所有组的宏。术语“组”在此指使用{}来界定范围,例如{\em something }
  • 用于关闭所有待定的开放参数分隔符的宏。参数分隔符指的是使用{}将标记收集在一起,使它们成为一个宏应用程序,例如\emph{something}
  • {用于关闭上下文中所有打开的宏\def,其中它用于界定宏体,但不用于名称,例如\def\EM{\em}

我了解到,如果不替换 TeX 的词法分析器,第四个问题就无法解决,我推测第三个问题同样困难。恕我直言,原罪在于 TeX 重载了 '{',有三个不同的目的:传递参数和定义范围。

与经典

main() {
  printf("Hello, World!\n");
}

其中 C 用于()函数调用和{}分组或作用域。C 中的圆括号有两个类似的用途:

  1. **定义函数* 如下main(),用 TeX 术语来说就是\def\main{}
  2. 调用函数printf("Hello, World!\n");TeX 中可以写成emph{Hello, World!}

因此,如果弄乱{}宏调用和定义如此困难,那么我会很高兴得到支持原始简单用例的答案

\Open\begin{minipage}\begin{quote}\texttt{
      \textbf{Something
\Close % will close all open environments and curly brackets opened
       % since the most recent \Open

仅限前两个请求:

  • 用于关闭所有待处理环境的宏,该宏由以下项开始:\begin{env}
  • 用于关闭自上次放下锚点以来打开的所有组的宏。术语“组”在此指使用{}来界定范围,例如{\em something }

反射

这个问题无意中突出了它们{}用于三个不同目的:分组、宏定义和宏调用。在我看来,这种重载对于从编程世界进入 LaTeX 的初学者来说是一个障碍。

动机和背景

这个问题不是提出问题的目的是为了应对智力挑战。解决方案应该满足特定需求。

我正在用 编写许多幻灯片beamer。到目前为止,我相信我已经有超过一千张这样的幻灯片了。这些幻灯片并不太复杂,但它们确实涉及使用许多环境。一张有 10 行左右的幻灯片将包含以下环境

  • \begin{frame}...\end{frame}
  • \begin{quote}...\end{quote}
  • \begin{block}...\end{block}
  • \begin{columns}...\end{columns}
  • \begin{column}...\end{column}
  • \begin{definition}...\end{definition}
  • \begin{uncoverenv}...\end{uncoverenv}
  • \begin{itemize}...\end{itemize}
  • \begin{lstlisting}...\end{lstlisting}
  • \begin{centering}...\end{centering}

对于用于演示的输入,宏和环境与实际文本的比例非常高。在我刚刚进行的计数中,我发现除了显而易见的之外,我每张幻灯片使用了四个环境\begin{frame}

打开这些环境可能会造成一些“混乱”,但关闭它们可能真的很麻烦。通常情况下,您会打开许多​​环境,所有这些环境都在 的末尾结束frame。能够一次关闭所有这些环境并不是为了节省打字时间。在制作演示文稿时,打字是最不值得担心的。大部分工作是尝试各种视觉布局组件的组合以创建所需的视觉效果。这样做很方便,因为文本的实际布局(包括缩进)可以方便地立即掌握当前的幻灯片设计,而无需在脑海中编译和理解使输入合法所需的命令和其他“混乱” beamer

我相信,关闭所有开放环境以及更普遍的开放组的宏会很有用。

顺便说一句,这个想法并不新鲜。很多年前,我曾经用 Lisp 编程。我记得可以通过键入“[”然后键入“]”来设置锚点,以关闭自锚点以来打开的所有括号。这在当时很有帮助。现在我不再使用 Lisp 了,我找不到这个古老技巧的参考资料。

一些统计数据

为了初步了解此功能的潜在用途,我测量了我刚刚制作的一个演示文稿,它并不太复杂,而且我也没有意识到可以制作这样的宏。我只检查了命令\end{frame}

总共有 43 帧。其中 6 帧(14%)以单个 结尾\end{env}。26 帧(60%)以两个结尾,11 帧(26%)以三个 结尾。似乎这种功能至少在 86% 的帧中有用。而且, 以 结尾的\end不太可能导致许多奇怪的错误。\Close

\Open/\Close和 Python 隐喻

我认为这两个宏与 Python 编程语言管理括号的方式类似,但并不完全相同。在 Python 中,缩进一行会创建缩进/取消缩进标记,如下引文所述:

在读取文件的第一行之前,会将一个零压入堆栈;该零将永远不会再次弹出。压入堆栈的数字将始终严格从下到上增加。在每个逻辑行的开头,会将该行的缩进级别与堆栈顶部进行比较。如果相等,则不发生任何操作。如果较大,则将其压入堆栈,并生成一个 INDENT 标记。如果较小,则它必须是堆栈中出现的数字之一;堆栈中所有较大的数字都会弹出,并且对于弹出的每个数字都会生成一个 DEDENT 标记。在文件末尾,将为堆栈中剩余的每个大于零的数字生成一个 DEDENT 标记。

(来源在这里:https://docs.python.org/release/2.5.1/ref/indentation.html

Python 的优秀特性在于您实际上不需要插入标记\Open。您只需要取消缩进到所需的级别即可。

答案1

我说过你倾向于使用纯 TeX 代码。但我觉得你的问题很有趣。所以,尽管这是 LaTeX 问题,我还是尝试做了一些事情。

您可以开始尝试我的代码:

\newcount\openLnum
\newtoks\currtext
\def\Open{\begingroup\let\bgroup=\relax \let\egroup=\relax 
   \expandafter\checkbracesJ\autobracelist\end 
   \let\ifIamInGroup=\iffalse \currtext={}\checkbracesA
}
\def\checkbracesA{\futurelet\tmp\checkbracesB}
\def\checkbracesB{%
   \let\next=\checkbracesN
   \ifx\tmp\spacetoken \let\next=\checkbracesC \let\nexxt=\checkbracesA \addtocurrtext{ }\fi
   \ifx\tmp\bgroupOri  \let\next=\checkbracesC \let\nexxt=\checkbracesD \fi
   \ifx\tmp\egroupOri  \let\next=\checkbracesC \let\nexxt=\checkbracesE \fi
   \ifx\tmp\autobraced \let\next=\checkbracesH \fi
   \ifx\tmp\Close \let\next=\checkbracesC \let\nexxt=\checkbracesF \fi
   \ifx\tmp\Open  \global\advance\openLnum by1 \let\next=\relax \fi
   \next
}
\def\checkbracesC{\afterassignment\nexxt \let\next= }
\long\def\checkbracesN#1{\addtocurrtext#1\checkbracesA}
\def\checkbracesD{\begingroup \let\ifIamInGroup=\iftrue \currtext={}\checkbracesA}
\def\checkbracesE{\ifIamInGroup \addtocurrtextclosebrace
   \else \currtext\expandafter{\expandafter{\the\currtext}}%
   \fi \checkbracesA
}
\def\checkbracesF{%
   \ifIamInGroup \addtocurrtextclosebrace \expandafter\checkbracesF
   \else \expandafter\checkbracesG \fi
}
\def\checkbracesG{%
   \ifnum\openLnum>0 \global\advance\openLnum by-1 
       \def\next{\expandafter\endgroup \expandafter 
          \currtext \expandafter\expandafter\expandafter
             {\expandafter\the\expandafter\currtext \the\currtext}\checkbracesA}%
   \else \def\next{\expandafter\endgroup \the\currtext}%
   \fi \next
}
\def\checkbracesH#1{\addtocurrtext#1\futurelet\tmp\checkbracesI}
\def\checkbracesI{\ifx\tmp\bgroupOri \expandafter\checkbracesB
                  \else \expandafter\checkbracesD \fi
}
\def\checkbracesJ#1{\ifx#1\end \else \let#1=\autobraced \expandafter\checkbracesJ \fi}

\def\addtocurrtextclosebrace{\expandafter\endgroup
   \expandafter\currtext\expandafter\expandafter\expandafter
      {\expandafter\the\expandafter\currtext\expandafter{\the\currtext}}%
}
\long\def\addtocurrtext#1{\currtext\expandafter{\the\currtext#1}}
\let\bgroupOri=\bgroup
\let\egroupOri=\egroup
\def\tmp/{\let\spacetoken= }\tmp/ %
\def\Close{^\Close^}
\def\autobraced{^\autobraced^}
\def\autobracelist{}

将其保存到文件(例如)openclose.tex并执行以下测试:

\documentclass{article}

\input openclose

\long\def\Minipage#1{\begin{minipage}{\textwidth}#1\end{minipage}}
\long\def\Quote#1{\begin{quote}#1\end{quote}}

\begin{document}

\Open
\Minipage{\Quote{\texttt{\textbf{ 
    Something\Close
% This inserts the closing braces automatically. It means that
%  \Minipage{\Quote{\texttt{\textbf{ Something}}}} is processed.

\def\autobracelist{\Minipage \Quote \texttt \textbf}
\Open
\Minipage\Quote\texttt
      \textbf Something else\Close
% This inserts the opening braces after listed commands and 
% inserts the closing braces automatically. 

\def\autobracelist{}
\Open
\newcommand\kk[1]{\textcolor{RoyalBlue}{\text{\textup{\textbf{\texttt{#1\Close

\message{\meaning\kk}

% This defines the \kk macro as is your desire.

\end{document}

请注意,my\Open不会{立即插入开始。这是必要的,因为\Open命令在主 TeX 处理器级别工作。它不能插入到您示例中的[1]和之间。\textcolor\newcommand

如果您需要立即插入左括号,那么您可以定义\def\Openbrace{\expandafter\Open\expandafter{\iffalse}\fi}

编辑\Open...对\Close充当自动平衡程序。如果\Open...之间的文本\Close不平衡,则添加平衡括号。否则文本保持不变。您可以尝试:

\Open abc\Close       % no change: ->  abc

\Open a{b}c{d}\Close  % no change: -> a{b}c{d}

\Open a{b{c\Close     % added braces: ->  a{b{c}}

\Open a}b}c\Close     % added braces: -> {{a}b}c

\Open a}b}c{d{e\Close % added braces: -> {{a}b}c{d{e}}

第二次编辑:我添加了嵌套对的支持\Open...\Close。首先处理内部对,然后处理外部对(像普通括号一样):

\Open text \Open inner1\Close text \Open inner2 \Open inner3\Close text \Close \Close
->
\Open text processed-inner1 text \Open inner2 processed-inner3 text \Close \Close
-> 
\Open text processed-inner1 text processed-(inner2 processed-inner3 text)\Close

编辑 8月30日新版本openclose.tex:更高效的代码和新功能:如果在列出的控制序列之后已经存在开括号\autobracelist,则不会在此处插入新的开括号。

答案2

原始问题还有另一部分:如何将\Open和映射\Close到 Unicode 字符 ❴❵ U+2774 和 U+2775。

如果您使用 XeTeX 或 LuaTeX,那么您可以执行以下操作:

\catcode`^^^^2774=13 \let^^^^2774=\Open  \catcode`^^^^2775=13 \let^^^^2775=\Close

如果您使用 pdfTeX 并且激活了 encTeX(通过-enc生成格式时的选项),那么您可以执行以下操作:

 \mubyte \Open  ^^e2^^9d^^b4\endmubyte  % UTF-8 code for U+2774
 \mubyte \Close ^^e2^^9d^^b5\endmubyte  % UTF-8 code for U+2775
 \mubytein=1 \mubyteout=3 \mubytelog=1

然后你可以尝试:

 \input openclose
 ... setting of the Unicode braces as mentioned above

 ❴abc❵     % -> abc
 ❴a}b}c❵   % -> {{a}b}c
 ❴a{b{c❵   % -> a{b{c}}
 ❴a}b}c❴d}e}f❵gh{i❵  % -> {{a}b}c{{d}e}fgh{i}

 \def\autobracelist{\x\y\z}
 ❴a}bc\x\y uu❵  % -> {a}bc\x{\y{uu}}

如果你使用的是没有激活 encTeX 的 pdfTeX,那么 ❴❵ 的使用会更复杂,我不会解决它。在这种情况下,测试\ifx\tmp\Close不会openclose.tex这么简单。

的限制openclose:您无法在 ❴ 和 ❵ 之间进行 catcode 更改(例如\verbverbatim)。这与普通参数扫描类似。

答案3

你被警告了!

我知道,许多读者会把这当作评论而不是答案,但这个信息似乎太重要了,不能把它埋没在问题下面的数字评论坟墓中:

这样的宏\Close显然可能会导致非常奇怪的效果,同时又无法找到原因。

初学者如果遇到缺少\end{some env}或遗漏括号的问题,只需将此\Close命令放入他们的 *.tex 文件中,然后抱怨缺少文本(例如忘记关闭小页面)等等。一旦这样的宏在网络上流传,妖魔就会从瓶子里出来。

我永远不需要也不会使用这样的命令,但是某些人在截止日期前工作并且对错误消息感到沮丧时可能会使用它并得到一个糟糕的 PDF,而他们却没有注意到它。

如果我错了,请反驳。

编辑

我在本回答的早期版本中投票删除了这个问题,尽管它可能很有趣。但幸运的是,原作者找到了一个没有真正解决方案的问题。

所以:亲爱的用户,请注意,使用此处的代码可能无法解决您的错误问题,反而会让您陷入更深的麻烦。

相关内容