\pgfutil@ifnextchar 实现中 \: 有何特殊之处

\pgfutil@ifnextchar 实现中 \: 有何特殊之处

中有以下代码片段pgfutil-common.tex(与 pgfmathutil.code.tex` 中的代码片段非常相似):

{%
  \def\:{\global\let\pgfutil@sptoken= } \:
  \def\:{\pgfutil@xifnch} \expandafter\gdef\: {\futurelet\pgfutil@let@token\pgfutil@ifnch}
}

我一直试图通过编写自己的版本来理解这一点:

\documentclass{article}
\makeatletter

\newcommand\aetest{\ae@test}
\def\ae@test(#1){%%
  \textsf{#1}%%
  \ae@ifnextchar H%%
  {\ae@true}{\ae@false}}

\def\ae@true#1;{ \textbf{(true):#1}}
\def\ae@false#1;{ \textbf{(false):#1}}

\long\def\ae@ifnextchar#1#2#3{%%
  \let\ae@reserved@d=#1%%
  \def\ae@reserved@a{#2}%%
  \def\ae@reserved@b{#3}%%
  \futurelet\ae@let@token\ae@ifnch}
\def\ae@ifnch{%%
  \ifx\ae@let@token\ae@sptoken
    \let\ae@reserved@c\ae@xifnch
  \else
    \ifx\ae@let@token\ae@reserved@d
      \let\ae@reserved@c\ae@reserved@a
    \else
      \let\ae@reserved@c\ae@reserved@b
    \fi
  \fi
  \ae@reserved@c}
{%%
  \def\:{\global\let\ae@sptoken= } \: 
  \def\:{\ae@xifnch} \expandafter\gdef\: {\futurelet\ae@let@token\ae@ifnch}%%
}

\makeatother
\begin{document}

\aetest(This test is) Hello;

\aetest(This test is) Ciao;

\end{document}

上述代码编译正确,符合预期。

上述代码片段已重写为:

{%%
  \def\:{\global\let\ae@sptoken= } \: 
  \def\:{\ae@xifnch} \expandafter\gdef\: {\futurelet\ae@let@token\ae@ifnch}%%
}

我相信我明白这个片段应该做什么:

(a)\let将令牌\ae@sptoken转换为空间令牌

(b)向前扫描标记列表中的任何空间标记到下一个非空间标记。

我有两个问题:

(1)为什么需要使用它\:来代替其他控制序列?

\expandafter\gdef\:(2)为什么扩展之后后面的空间没有被吃掉?

在《TeXBook》第 39 页,Knuth 写道:

.... 标记列表内的控制序列后的空格不会被忽略;忽略空格规则仅适用于输入文件,在字符串被标记期间。

因此,我认为可以将此代码片段的第一行重写为:

\def\ae@colon{\global\let\ae@sptoekn= }\ae@colon

我的印象是,由于等号后面的空格已经被标记化,所以它不会丢失。但似乎正在发生的事情是,当\ae@colon扩展时,这个标记化的空间是无关紧要的。相反, \ae@sptoken\let\def。虽然我不确定如何验证这一点,但 LaTeX 只是抱怨\ae@xifnch未定义。因此我的第一个问题。

关于\expandafter\gdef\:以下空间:我认为这个空间应该丢失,因为\:在空间被标记之前它被扩展了。因此,我认为以下方法之一可能有效:

\expandafter\gdef\:{....} %%<-- no space following "\:"

或者

\gdef\ae@xifnch{....}

或者

\gdef\ae@xifnch {...} %%<-- space after \ae@xifnch

但两者都不起作用。相反,LaTeX 挂起了。我认为这是因为它一直在解析相同的空格标记,并且无法将其从列表中删除。

因此,当\expandafter\gdef\:扩展时,以下空间会保留下来。因此我的第二个问题是。

关于冒号的选择,我知道 没什么特别的\:。我尝试过\!\,。两者都工作得很好。所以,这里的技巧与控制符号(由一个非字母组成的控制序列)的工作方式有关。但是,我不知何故不知道如何\:在这里实现预期的效果。

答案1

我想我现在明白这里发生了什么。

语法\let如下

\let<control sequence><equal><one optional space><token>

如果我写,其中*表示空格

\let\ae@sptoken=*

然后*就被解释为<one optional space>并且下一个标记被抓取用于\letting。

在 的情况下\def\:{\let\ae@sptoken= }, 后面的空格=<one optional space>。现在由于\:是命令符号,其后面的空格将转换为空格标记。假设当 LaTeX 到达行末时,它会将行末字符转换为空格(前提是 LaTeX 处于 状态M)。这意味着

\:

扩展为

\let\ae@sptoken=**

(我再次使用*来表示空间)。空间 也是\ae@sptoken如此。\let

通过以下方式可以达到类似的效果:

\def\ae@colon{\let\ae@sptoken= }
\expandafter\ae@colon\space

这里的诀窍是找到一种方法来在紧随之后的两个相邻空间中走私=

关于

\expandafter\gdef\: {....}

我误解了空格转换为标记的时间。转换发生在扩展之前\expandafter。因此,当上面的行扩展时,它变成

\gdef\ae@xifnch*{....}

其中*表示空间,现在它是一个标记,不会被命令字符串吞噬。

通过以下方式可以达到类似的效果:

\expandafter\gdef\expandafter\ae@sptoken\space{....}

相关内容