我正在尝试理解etoolbox
\ifdefempty
和\ifdefvoid
宏。文档说\ifdefempty
如果控制序列已定义并且是替换文本为空的无参数宏,则扩展为 true,否则扩展为 false。
相似地\ifdefvoid
如果控制序列未定义,则扩展为 true;如果是含义为 的宏
\relax
,或替换文本为空的无参数宏,则扩展为 false。
根据这些描述,我认为\def\myempty{}
、\def\myrelax{\relax}
和\def\myemptyempty{\myempty}
都既是“空”又是“无效”,但\def\myempty{}
是唯一一个既是“空”又是“无效”的。我遗漏了什么?
\documentclass{minimal}
\usepackage{etoolbox}
\begin{document}
\def\myempty{}
\def\myrelax{\relax}
\def\myemptyempty{\myempty}
\ifdefempty{\myempty}{Empty}{Not empty}
\ifdefempty{\myrelax}{Empty}{Not empty}
\ifdefempty{\myemptyempty}{Empty}{Not empty}
\ifdefvoid{\myempty}{Void}{Not void}
\ifdefvoid{\myrelax}{Void}{Not void}
\ifdefvoid{\myemptyempty}{Void}{Not void}
\end{document}
答案1
假设通过了(已定义且无参数)\<cs>
的前两个测试,则在内执行下一个测试 - 替换文本是否为空。此测试检查的是否为“空白”:\ifdefempty
\etb@ifdefempty
\meaning
\<cs>
\def\etb@ifdefempty#1{%
\expandafter\expandafter
\expandafter\ifblank
\expandafter\expandafter
\expandafter{%
\expandafter\strip@prefix\meaning#1}}
这里的“空白”是根据 的 进行检查的。\strip@prefix
这只是从 的输出中删除了 部分(因此更改为)。因此,在\meaning
#1
macro:->
\meaning
macro:->\relax
\relax
\myempty
,检查\ifblank{}
哪个为真。\myrelax
,检查结果\ifblank{\relax}
为假。\myemptyempty
,支票是\ifblank{\myempty}
,这是错误的。
以下更新版本\ifdefempty
实际上向您展示了所执行的比较:
\makeatletter
\renewcommand{\ifdefempty}[1]{
{\ttfamily\expandafter\strip@prefix\meaning#1\quad}%
\ifundef{#1}
{\@secondoftwo}
{\ifdefmacro{#1}
{\ifdefparam{#1}
{\@secondoftwo}
{\etb@ifdefempty{#1}}}
{\@secondoftwo}}}
\makeatother
的内部工作\ifdefvoid
原理类似,因为它使用相同的最内层函数进行测试,即\etb@ifdefempty
。以下更新版本\ifdefvoid
突出显示了测试针对的内容:
\makeatletter
\renewcommand{\ifdefvoid}[1]{%
{\ttfamily\expandafter\strip@prefix\meaning#1\quad}%
\ifundef{#1}
{\@firstoftwo}
{\ifdefmacro{#1}
{\ifdefparam{#1}
{\@secondoftwo}
{\etb@ifdefempty{#1}}}
{\@secondoftwo}}}
\makeatother
答案2
宏是带有替换文本的无参数宏。您可以获得具有以下\def\myrelax{\relax}
含义的命令序列:\myrelax
\relax
\relax
\let
\let\myrelax\relax
还定义了一个非空的\def\myemptyempty{\myempty}
无参数宏,其替换文本为。首先展开替换文本:\myemptyempty
\myemptyempty
\edef
\edef\myemptyempty{\myempty}
概括:
\ifdefempty
抓住案例:
\myempty
:\def\myempty{}
以及\ifdefvoid
案例:
\UnDeFiNeDCoMmAnD
\myrelax
:\let\myrelax\relax
\myempty
:\def\myempty{}
答案3
我们不必深究其内部原理,只需分析记录的行为即可。
\ifdefempty
:如果控制序列已定义并且是替换文本为空的无参数宏,则扩展为 true,否则扩展为 false。
这意味着\ifdefempty{\cs}
测试是否\cs
是一个宏(即可扩展的非原始控制序列),不带参数,并且其第一级扩展为空。也就是说,它应该通过以下方式创建
\def\cs{}
\edef\cs{<stuff expanding to empty text>}
\let\cs<control sequence for which \ifdefempty{} is true>
\ifdefvoid
:如果控制序列未定义,或者是一个含义为 \relax 的宏,或者是一个替换文本为空的无参数宏,则扩展为 true,否则扩展为 false。
这将 的测试扩展\ifdefempty
为包括未定义的控制序列(与原始和不可扩展相反,它们在本测试的意义上不是 void),或等同于\relax
。在后一种连接中使用“宏”一词是不幸的,因为它们不是技术意义上的宏(不可扩展)。这样的控制序列应该通过以下方式创建:
<any of the above for \ifdefempty>
\let\cs\relax
\csname cs\endcsname %If \cs were previously undefined, this makes it \relax
或者,当然,绝不已创建,因此仍未定义。