是否有可能在不扩展令牌寄存器的情况下测试它是否为空?
答案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}
。