了解空参数检查

了解空参数检查

我遇到了以下检查空参数的代码:

\catcode`\@=11 % as in plain.tex
\long\def\blank#1{\bl@nk#1@@..\bl@nk}%
\long\def\bl@nk#1#2@#3#4\bl@nk{#3#4}
\catcode`\@=12

\long\def\test#1{\begingroup \toks0{[#1]}%
  \newlinechar`\/\message{/\the\toks0:
  \if\blank {#1}EMPTY\else NOT empty\fi%

有人可以解释一下它是如何工作的吗?

尤其是这部分我不太理解。如果用vs\if\blank {#1}调用,它会扩展成什么?\test{}\test{Test}

答案1

\catcode`\@=11 % as in plain.tex
\long\def\blank#1{\bl@nk#1@@..\bl@nk}%
\long\def\bl@nk#1#2@#3#4\bl@nk{#3#4}
\catcode`\@=12

\long\def\test#1{\begingroup \toks0{[#1]}%
  \newlinechar`\/\message{/\the\toks0:
  \if\blank {#1}EMPTY\else NOT empty\fi%

在上面的代码中,你会发现短语“blank”:

在口语化的 TeX 语言中,如果宏参数根本不包含任何标记,即为空,或者仅由类别代码 10(空格)和字符代码 32 的显式字符标记组成,即仅由显式空格标记组成,则宏参数被称为“空白”。 (字符标记的字符代码是指字符在 TeX 的内部字符表示方案中的数字,在传统的 TeX 引擎中为 ASCII,而在 XeTeX/LuaTeX 中为 Unicode,其中 ASCII 是其严格子集。)

测试是为了查明是否\if\blank{1⟨\blank's argument⟩}2E11M11P11T11Y11\elseN11O11T11explicit space token10e11m11p11t11y11\fi⟨\blank 的论点⟩为空或仅由明确的空格标记组成(字符代码 32、类别代码 10)。

测试依赖于\if\blank{1⟨\blank's argument⟩}2E11M11P11T11Y11\elseN11O11T11explicit space token10e11m11p11t11y11\fi⟨\blank 的论点⟩不包含标记或标记,因为这些标记由 -macro 传递并用作参数分隔符和来自用户输入/来自的标记@11\bl@nk\blank\bl@nk⟨\blank 的论点⟩不应错误地匹配这些分隔符。

TeX 工作方式的两个微妙方面被利用:

  1. \ifTeX 在收集-comparison=character-code-comparison的两个标记时,会不断扩展可扩展的内容。
  2. 当 TeX 收集属于未限定参数的标记时,将丢弃该参数最外层之前/形成该参数的单个标记之前的所有显式空格标记。{1

\blank{1⟨\blank's argument⟩}2产量:

\bl@nk⟨\blank's argument⟩@11@11.12.12\bl@nk

测试的要点是关于 TeX 如何收集参数\bl@nk

如果为⟨\blank's argument⟩空白,

  • 后面的第一个将被视为 的第一个未限定的)参数。@11⟨\blank's argument⟩\bl@nk
  • 后面的第二个将被视为 的第二(⁠ -delimited)参数的分隔符,该参数为空。@11⟨\blank's argument⟩\bl@nk⁠@11
  • 后面第一个将被视为 的第三个未限定)参数。.12⟨\blank's argument⟩\bl@nk
  • 后面的第二个将用作 的第四(⁠ -delimited)参数。.12⟨\blank's argument⟩\bl@nk⁠\bl@nk

\bl@nk的第三第四个参数被传递,因此你有类似
\if.12.12E11M11P11T11Y11\elseN11O11T11explicit space token10e11m11p11t11y11\fi

将 的字符代码与 的字符代码进行比较,因此采用 -branch/true-branch 并丢弃 -branch/false-branch。.12.12\if\else

如果⟨\blank's argument⟩不是空白,

  • 第一个不是显式空格标记的标记(即,不是字符代码 32 和类别代码 10(空格)的显式字符标记)或第一个-group 的标记将被视为 的第一个未分隔的)参数。{1}2⟨\blank's argument⟩\bl@nk
  • 后面的第一个将作为 的第二(⁠ -delimited)参数的分隔符,该分隔符保存了 的余数。@11⟨\blank's argument⟩\bl@nk⁠@11⟨\blank's argument⟩
  • 后面的第二个将被视为 的第三(未限定)参数。@11⟨\blank's argument⟩\bl@nk
  • 后面的标记序列将用作 的4个(⁠分隔的)参数。.12.12⟨\blank's argument⟩\bl@nk⁠\bl@nk

\bl@nk的第三第四个参数被传递,因此你有类似
\if@11.12.12E11M11P11T11Y11\elseN11O11T11explicit space token10e11m11p11t11y11\fi

将 的字符代码与 的字符代码进行比较,因此-branch/true-branch 被丢弃并且-branch/false-branch 被采用。@11.12\if\else


答案TeX 如何查找分隔参数?你可能会感兴趣

答案空标记列表的可扩展测试——方法、性能和稳健性你可能会感兴趣

答案2

如果#1为空则

\if\blank{}

\if\bl@nk @@..\bl@nk

\if ..

如此真实

如果#1非空,!则说

\if\blank{|}

\if\bl@nk !@@..\bl@nk

\if @..

这是错误的,所以.跳到\else


请注意,此测试并不完全安全


\catcode`\@=11 % as in plain.tex
\long\def\blank#1{\bl@nk#1@@..\bl@nk}%
\long\def\bl@nk#1#2@#3#4\bl@nk{#3#4}


\if\blank{@@}EMPTY\else Not empty\N\fi


\bye

将按..EMPTY 内部分隔参数的格式进行排版,并假设被测试的参数从不包含 catcode 11 @

答案3

要记住的第一件事是,\if将进行递归宏扩展,直到找到两个不可扩展的标记。

第二个重要事实是,TeX 在寻找未限定的宏参数时会忽略显式的空格标记。

让我们看看调用时发生了什么

\if\blank{a}<true>\else<false>\fi
\if\blank{}<true>\else<false>\fi
\if\blank{ }<true>\else<false>\fi

\if\blank{a}<true>\else<false>\fi

TeX 将会扩展\blank,因此#1将会a得到

\if\bl@nk a@@..\bl@nk•<true>\else<false>\fi

(第一个 之后没有空格,我使用它只是为了查看标记的结束位置)。现在将扩展\bl@nk宏。它会查找未定界的参数,然后查找由 定界的参数,然后查找未定界的参数,最后查找由 定界的参数。在本例中,\bl@nk@\bl@nk

#1 <- a
#2 <-    (empty)
#3 <- @
#4 <- ..

并将替换文本,因此我们得到

\if @..<true>\else<false>\fi

由于@.没有相同的字符代码,测试将返回 false,因此

<false>\fi

将保留在输入流中。

\if\blank{a}如果我们有而不是 ,结果会类似\if\blank{abc},因为在这种情况下我们会得到

#1 <- a
#2 <- bc
#3 <- @
#4 <- ..

\if\blank{}<true>\else<false>\fi

第一步我们得到

\if\bl@nk @@..\bl@nk<true>\else<false>\fi

(再次强调,第一个空格之后的空格\bl@nk不存在)。现在\bl@nk寻找它的参数:

#1 <- @
#2 <-    (empty)
#3 <- .
#4 <- .

因此输入流将具有

\if..<true>\else<false>\fi

现在测试返回 true。

\if\blank{ }<true>\else<false>\fi

与前一种情况相同:我们得到

\if\bl@nk @@..\bl@nk<true>\else<false>\fi

但现在后面的空格\bl@nk是从 的参数中获得的\blank。没什么大不了的!上述第二条规则适用,该空格被忽略,其余内容与第二种情况完全相同。

笔记

正如 David Carlisle 指出的那样,这并不是特别稳健,因为如果在类别代码为 11 的上下文中使用@,即使参数不为空,测试也可能返回 true,如果我们调用\if\blank{@@}。事实上,在这种情况下,第一步会产生

\if\bl@nk @@@@..\bl@nk<true>\else<false>\fi

我们有

#1 <- @
#2 <-    (empty)
#3 <- @
#4 <- @..

所以我们最终会得到

\if @@..<true>\else<false>\fi

测试将返回 true。两个句点将出现在输入流中。

更安全的测试是很奇怪字符,例如Q通常找不到的类别代码为 3 的字符。

相关内容