我一直误以为,\ifx
关于这个主题,你只需要记住通过“象征性平等”测试的东西,但这周我了解到了事实并非如此(感谢大卫卡莱尔)。
现在我想知道有多少个根本不同的“代币等价”概念?
到目前为止我知道两个:
用 进行测试
\ifx
。当两个字符具有相同的字符代码和 catcode 时,它们是相等的;当两个 cs 名称都未定义或都“让给同一个事物”时,它们是相等的;当一个 cs 名称曾经到过一个字符时,它就等于该字符
\let
,...在分隔参数中测试。
两个字符具有相同的字符代码和 catcode 时,它们相等;两个 cs 名称具有相同的字符代码和 catcode 时,它们相等。有相同的名字,一个字符永远不会等于一个 cs 名称,...
但我的知识显然是零散的。我希望了解全部情况。
在 TeXbook 中很难找到这样的内容。
我想针对每个根本不同的令牌平等概念寻求一个答案。
每个答案都应明确说明
- 在什么情况下会调用这个概念。
- 哪些标记相等,哪些标记不相等。
- 这是任意的还是背后有某种原理(而不是像 那样让一切正常工作
\ifx
)。 - 有没有什么巧妙的技巧可以利用这一点?
只是要确定:我还想就我提到的两个例子得到答案,因为我的知识显然有限......
答案1
我认为最好的思考方式是,标记的“基本相等性”是,如果字符标记具有相同的字符代码和 catcode,则它们相等。如果命令标记具有相同的名称,则它们相等。
然后,分隔宏解析需要相等的标记。
\ifx
测试两个标记的“定义”是否相等。对于宏,定义是其定义中的标记列表(第一级扩展);对于基元,每个基元都有唯一的定义;对于字符标记(以及让命令标记成为字符标记),定义封装了字符和 catcode。
\if
不同于\ifx
使用扩展来确定要测试的标记的方式,但除此之外,它使用了一种修改后的相等形式,其中仅考虑字符标记的字符代码而不是 catcode,并且所有命令标记而不是\let
字符标记都被视为相等。
\ifcat
与之相同,\if
只是它使用的是 catcode 而不是字符代码。
答案2
⟨令牌 1⟩是相同的⟨代币 2⟩如果和
\newtoks\Scratchtoks
和和和则 测试 结果为 。
\Scratchtoks{⟨token 1⟩}\edef\tempa{\the\Scratchtoks}
\Scratchtoks{⟨token 2⟩}\edef\tempb{\the\Scratchtoks}
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}
\ifx\tempa\tempb\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
\firstoftwo
陷阱/问题:
该测试不可扩展,因为需要临时分配( \Scratchtoks
,\tempa
和)。\tempb
⟨令牌 1⟩和/或⟨代币 2⟩被定义是\outer
一个问题。
⟨令牌 1⟩和/或⟨代币 2⟩作为 catcode 1 或 2 的明确字符标记是一个问题。
\ifx⟨token 1⟩⟨token 2⟩...
测试是否⟨令牌 1⟩和⟨代币 2⟩有同样的含义。
\if⟨token 1⟩⟨token 2⟩...
测试是否⟨令牌 1⟩和⟨代币 2⟩具有相同的字符代码。可扩展令牌在收集时会扩展⟨令牌 1⟩和⟨代币 2⟩。 不明确的⟨控制序列标记⟩产生错误消息! Undefined control sequence.
,除非通过在它们前面加上 来阻止它们的扩展,\noexpand
在这种情况下它们被视为非隐式字符标记的不可扩展控制序列标记。所有非隐式字符标记的不可扩展控制序列标记都被假定具有相同的字符代码 - 没有字符所具有的字符代码。对于隐式字符,无论是控制符号标记还是控制字标记或活动字符标记,都假定它们等于字符标记的字符代码。如果未定义的活动字符前面有 ,则假定它们具有其非活动悬垂部分的字符代码\noexpand
。
\ifcat⟨token 1⟩⟨token 2⟩...
测试是否⟨令牌 1⟩和⟨代币 2⟩具有相同的类别代码。可扩展令牌在收集时会扩展⟨令牌 1⟩和⟨代币 2⟩。 不明确的⟨控制序列标记⟩产生错误消息! Undefined control sequence.
,除非通过在它们前面加上 来阻止它们的扩展,\noexpand
在这种情况下它们被视为非隐式字符标记的不可扩展控制序列标记。所有非隐式字符标记的不可扩展控制序列标记都被认为是具有相同的类别代码 - 没有字符具有的类别代码。对于隐式字符,无论是控制符号标记还是控制字标记或活动字符标记,都假定它们等于字符标记的类别代码。如果未定义的活动字符前面有 ,则假定其类别代码为 13(活动)\noexpand
。
如果控制符号标记和控制字标记具有相同的名称,则它们是相等的。即,如果应用\string
产生相同的字符标记序列。
除了一些极端情况:
无名控制序列标记可以通过两种方式创建:
- 通过
\csname\endcsname
。 - 通过类别代码 0(转义)的单个字符,即,
\
位于 .tex 输入行的末尾,而整数参数的值\endlinechar
不在字符可能的编码点范围内。
应用于\string
无名控制序列会产生一系列字符标记:
⟨current escapechar-token⟩c12s12n12a12m12e12⟨current escapechar-token⟩e12n12d12c12s12n12a12m12e12
⟨current escapechar-token⟩
如果整数参数\escapechar
不在字符可能的编码点范围内,则表示根本没有标记/没有任何标记。
如果整数参数\escapechar
在字符的可能编码点范围内,则⟨current escapechar-token⟩
表示一个字符标记,其字符代码等于的值\escapechar
,并且如果\ecapechar
值为 32,则其类别代码为 10(空格),如果值不同于\ecsapechar
32,则其类别代码为 12(其他)。
因此,\string
仅使用 就无法区分无名控制序列和名称为 的控制字标记。
csname⟨character denoted by current value of \escapechar⟩endcsname
在两个标记被赋予相同含义的边缘情况下,\ifx
也不适合区分它们。
你可以区分它们
- 通过分隔参数。
- 通过定义临时宏并
\ifx
对其进行比较。
下面的例子
\expandafter\def\csname\endcsname{Some Definition.}
\begingroup
\def\firstofone#1{#1}
\catcode`\/=0
\catcode`\\=11
/firstofone{%
/endgroup
/def/csname\endcsname{Some Definition.}%
}%
\endlinechar=-1\relax
\message{^^J^^JStringification:^^J}
\message{^^J|\string\
|}
\message{^^J|\expandafter\string\csname csname\string\endcsname\endcsname|}
\message{^^JMeaning:^^J}
\message{^^J\meaning\
}
\message{^^J\expandafter\meaning\csname csname\string\endcsname\endcsname}
\message{^^JDirect \string\ifx-comparison:^^J}
\message{^^JTokens have %
\expandafter\ifx\csname csname\string\endcsname\endcsname\
equal meaning\else different meanings\fi.}
\message{^^J\string\ifx-comparison of temporary macros:}
\def\tempa{\
}%
\expandafter\def\expandafter\tempb\expandafter{\csname csname\string\endcsname\endcsname}%
\message{^^JTemporary macros defined from tokens have \ifx\tempa\tempb
equal meaning\else different meanings\fi.}
\message{^^J}
\csname stop\endcsname
\bye
在终端上产生以下消息:
Stringification:
|\csname\endcsname|
|\csname\endcsname|
Meaning:
macro:->Some Definition.
macro:->Some Definition.
Direct \ifx-comparison:
Tokens have equal meaning.
\ifx-comparison of temporary macros:
Temporary macros defined from tokens have different meanings.
如果字符标记具有相同的类别代码和相同的字符代码,则它们是相等的。
关键部分是找出一个标记是控制字标记/控制符号标记还是显式字符标记。
这很关键,因为存在极端情况。
例如,您无法区分等于其非活动挂件之一的活动字符标记和不等于其非活动挂件的挂件,除非使用由其中一个标记分隔的参数或定义如上所示的临时宏。
例如,之后
\begingroup
\catcode`\a=13
\@firstofone{\endgroup\let a=}a
只有通过使用由其中一个标记分隔的参数或定义临时宏(如上所示),才有可能区分 active-a 和 catcode-11(letter)-a。
\escapechar
这同样适用于具有负值的单字母控制字/符号。
例如,
\escapechar=-1\relax
\let\a=a
要与 catcode-11(letter)-a区分开来,\a
只有使用由其中一个标记分隔的参数或定义如上所示的临时宏才有可能。或者,您可以利用以下事实:虽然a
类别代码为 11(letter)\a
不是控制符号标记而是控制字标记,因此未扩展的书写\a
会产生一个在 之后带有空格字符的序列,a
而未扩展的书写a
会产生一个在 之后没有空格字符的序列a
。如果在两者之间将 a 的 catcode 切换为其他内容,例如 12(other),则此方法不起作用,因为\a
是控制符号标记,而 TeX 在未扩展的书写控制符号标记时不会附加空格字符。