测试令牌寄存器是否为空

测试令牌寄存器是否为空

是否有可能在不扩展令牌寄存器的情况下测试它是否为空?

答案1

在撰写本文时,本页上其他基于 TeX 的答案存在缺陷,因为它们将条件隐藏\ifx在宏中,但仍在“顶层”使用\else/ 。这意味着在其他条件中使用时会出现意外中断。\fi

LaTeX3 编程语言 expl3 包含一个用于处理标记寄存器的模块:

\usepackage{expl3}
...
\ExplSyntaxOn
\toks_if_empty:NTF \mytoks {true} {false}
\ExplSyntaxOff

它本质上在内部执行这里的其他答案所建议的操作,但它使用扩展来获取其参数,因此分支是强大的(并且您不必\fi担心会妨碍您)。

更新:那么这种方法比其他方法优越在哪里呢?考虑一下首先为回答这个问题而提供的解决方案风格:

\def\IfEmpty#1{%
  \edef\1{\the#1}
  \ifx\1\empty
}
...
\IfEmpty\foo T\else F\fi

在嵌套时,这种做法不太好,因为 TeX 在丢弃条件分支时会向前扫描。考虑

\ifx\bar\baz
  \IfEmpty\foo T\else F\fi % <- uh oh
\else
  E
\fi

如果\bar= \baz,则丢弃第二个分支并执行第一个分支。到目前为止一切顺利。如果\bar\baz,则通过向前读取直到第一个不匹配的分支来丢弃第一个分支\else——这就是上面标记为“uh oh”的行中的分支。因此,在这种情况下,您可以将上述代码片段的扩展折叠为:

\iffalse\else F\fi % <- uh oh
\else
  E
\fi

\else因此在这种情况下导致“额外”错误消息。

所以这种形式的条件语句效果不太好。下次再试。你也可以像这样编写这种风格的代码:

\def\IfEmpty#1#2#3{%
  \edef\1{\the#1}
  \ifx\1\empty
    #2%
  \else
    #3%
  \fi
}

这避免了上一个试验解决方案中的嵌套问题,但容易出现另一个问题:#2#3后面有尾随材料,即\else\fi。如果你想写类似的东西,这是一个问题

\def\processfoo#1{...something with #1...}
\IfEmpty\foo{\error}{\processfoo} {arg}

因为#1传递给的将\processfoo\fi而不是所需的{arg}。在这种情况下,条件最好写成

\def\IfEmpty#1#2#3{%
  \edef\1{\the#1}
  \ifx\1\empty
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {#2}{#3}
}

所以克服了这个问题。这就是 expl3 条件的工作方式,这就是为什么我们TF在所有名称的末尾都写下来以表示“真”和“假”分支。(或者只是T或只是F如果你只想要其中一个。)

顺便说一句,有可扩展的测试来检查是否为空,这就是为什么我建议使用 expl3 方法进行此测试。当然,可扩展性并不总是必需的,但完全可扩展的代码往往更可靠,并且对于以下情况来说,拥有它总是好的

\typeout{ \toks_if_empty:NT \foo {Warning:~\string\foo\space is~ empty} }

答案2

使用 LuaTeX 你可以这样做:

\directlua{
if string.len(tex.toks["headline"]) > 0 then
    print("not emtpy")
end
}

但由于我从未使用令牌列表:不要使用此代码来建造核电站。

答案3

Ulrich Diez 经常会发布comp.text.tex如下代码(我根据 toks 的需要对其进行了轻微的修改):

\newcommand\@ifempty@toks[1]{%
  \ifcat\relax\detokenize\expandafter{\the#1}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

或者,不使用 e-TeX,

\newcommand\@ifempty@toks[1]{%
  \ifcase\iffalse{{{\fi\expandafter\@ifempty@@toks\the#1}1}1}0
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\newcommand{\@ifempty@@toks}
  {\expandafter\@gobble\expandafter{\expandafter{%
        \ifcase`}\expandafter}\expandafter\fi\string}

编辑:Ulrich Diez 正确地指出,重新定义\relax可能会导致第一个测试失败。使用字符标记(例如$或)稍微安全一些X,其类别代码在 3、4、7、8、11 之间。

\toks此外,如果包含外部宏(如),则这两种方法都不起作用\outer\def\foo{} \newtoks\mytoks \mytoks=\expandafter{\noexpand\foo}

答案4

与 Jan Hlavacek 的答案类似,但可扩展:

\def\certainlynotintoks{\certainlynotintoks}
\def\iftoksempty#1{\expandafter\ifx\expandafter
    \certainlynotintoks\the#1 \certainlynotintoks}

\toks0{\undefined will not be expanded}
\toks2{}
\iftoksempty{\toks0} empty\else not empty\fi \par
\iftoksempty{\toks2} empty\else not empty\fi

这要求\certainlynotintoks不作为令牌寄存器中的第一个令牌出现。这个和 Jan 的答案都确实扩展了令牌寄存器,但只扩展了一次。我不认为有办法避免这种情况。

编辑:考虑到 Will 的(绝对正确的)评论,将的定义更改\iftoksempty

\makeatletter
\def\iftoksempty#1{\expandafter\ifx\expandafter
    \certainlynotintoks\the#1 \certainlynotintoks \expandafter\@firstoftwo
    \else \expandafter\@secondoftwo\fi}

然后您就可以使用\iftoksempty{\toks0}{empty}{not empty}

相关内容