pgfmanual-en-macros.tex 的第 1099-1121 行

pgfmanual-en-macros.tex 的第 1099-1121 行

我想了解如何排版命令以用于 LaTeX 文档。澄清一下,不是使用 LaTeX 创建文档,而不是用 LaTeX 编写有关使用 LaTeX 的文档(如手册中所述)。据我所知,没有这个术语来区分 LaTeX 文档和使用 LaTeX 创建文档(不幸的是)。

pgfmanual-en-macros.tex 的第 1099-1121 行

https://www.ctan.org/pkg/pgf?lang=en

这段代码“看起来”像是删除了 at 符号,但我不知道。为什么这是必要的?它是如何工作的?

{
  \makeatletter
  \global\let\myempty=\@empty
  \global\let\mygobble=\@gobble
  \catcode`\@=12
  \gdef\getridofats#1@#2\relax{%
    \def\getridtest{#2}%
    \ifx\getridtest\myempty%
      \expandafter\def\expandafter\strippedat\expandafter{\strippedat#1}
    \else%
      \expandafter\def\expandafter\strippedat\expandafter{\strippedat#1\protect\printanat}
      \getridofats#2\relax%
    \fi%
  }

  \gdef\removeats#1{%
    \let\strippedat\myempty%
    \edef\strippedtext{\stripcommand#1}%
    \expandafter\getridofats\strippedtext @\relax%
  }

  \gdef\stripcommand#1{\expandafter\mygobble\string#1}
}

推理

我之所以想了解这一点,是因为我似乎可以使用以下代码来排版命令。我怀疑在某些情况下我的想法不起作用。

但也存在局限性:

  • 包含带有参数的命令,以便我可以在解释下排版它们(在NewEnvironwith 中#1)作为示例(例如 \hello{input1} 然后使用#1inNewEnviron不起作用)

\documentclass{article}
\usepackage{fontspec}
\usepackage{environ}
\NewEnviron{command}[1]{% 
\begin{minipage}[t]{.3\textwidth}
\texttt{\string#1}
\end{minipage}
\hfill
\begin{minipage}[t]{.7\textwidth}
\BODY
\end{minipage}
\xdef\putcommandexample{\BODY}% Set BODY to variable http://tex.stackexchange.com/a/14392/13552
}%
\begin{document}
\section{Friendly Commands}
\begin{command}{\hello}
This command greets the reader in a friendly manner.
\end{command}
\begin{command}{\goodbye}
This command greets the reader in a friendly manner.
\end{command}
\end{document}

感兴趣的人请注意:除了minipages 之外,我还使用包将命令放在了边距中marginnote。看起来还不错。

\NewEnviron{command}[1]{% 
\reversemarginpar\marginnote{\texttt{\string#1}}
\BODY
\par
}%

输出

在此处输入图片描述

答案1

问题“为什么有必要?”需要阅读所有文档源。它的工作原理很容易理解。

该宏\getridofats决定输入流中跟随它的内容是否\relax包含类别代码 12 @

不过,最好先看看它是如何\removeats工作的。它应该接收一个命令名称作为其支撑参数;首先它初始化\strippedat为一个空的标记列表。然后它

\edef\strippedtext{\stripcommand#1}

将其设置\strippedtext为包含命令名称的字符串,其中去掉了反斜杠,因为它确实

\expandafter\mygobble\string#1

例如,\removeats{\abc@def}是否\expandafter\mygobble\string\abc@def首先将其转换\abc@def为第 12 类字符的字符串(但@如果在上下文中调用该宏,则后面出现的字母的类别代码为 11 \makeatother),然后\mygobble吃掉反斜杠。

经过此准备后,\expandafter\getridofats\strippedtext @\relax将被调用,并在输入流中产生以下标记:

\getridofats abc@def@\relax

查看 的定义\getridofats,我们发现#1abc,而#2def@。如果调用\removeats{\abcdef}被看到,输入流将包含

\getridofats abcdef@\relax

并且#1将是abcdef,而#2将是空的。

该宏\getridtest被定义为扩展为#2;在第一种情况下它不为空,在第二种情况下它为空。

此后,\ifx\getridtest\myempty在第二种情况下返回 true,在第一种情况下返回 false。

假设测试返回 true(第二种情况)。然后

\expandafter\def\expandafter\strippedat\expandafter{\strippedat abcdef}

执行,将#1(在本例中abcdef)附加到 的先前值\strippedat。它可能看起来没用,因为\strippedat已被初始化为空。但我们稍后会看到在“false”情况下会发生什么。

假设测试返回 false(第一种情况)。然后

  \expandafter\def\expandafter\strippedat\expandafter{\strippedat abc\protect\printanat}%
  \getridofats def@\relax

完成:将第一部分附加到\strippedat,然后回调\protect\printanat\getridofats来处理最后一块。

在我看来,这不是一个特别好的宏。然而,调用

\removeats\abcdef
\removeats\abc@def
\removeats\ab@cd@ef

分别导致\strippedat包含

abcdef
abc\protect\printanat def
ab\protect\printanat cd\protect\printanat ef

其中\printanat定义为

\def\printanat{\char`\@}

当然,\protect如果宏是用 定义的,那么肯定不需要\def\printanat{\char64 },但这是个人选择。由于文档是用 LaTeX 处理的,我可能会选择

\DeclareRobustCommand{\printanat}{\char`\@ }

(请注意尾随空格,没有它是错误的)。

显然,该机制用于避免@在文件中写入字符.aux,从而避免其类别代码出现问题(读取 LaTeX 辅助文件时设置为 11)。


expl3@版本,将在类别代码为 12 的环境中发布。

\ExplSyntaxOn
\cs_new_protected:Npn \removeats #1
 {
  \tl_set:Nx \strippedat { \cs_to_str:N #1 }
  \tl_replace_all:Nnn \strippedat { @ } { \protect\printanat }
 }
\ExplSyntaxOff

相关内容