解决:问题出在moderncv
我使用的版本太旧了。我在问题的末尾添加了一些额外的细节,但我将删除这个问题。感谢那些写信澄清\ifx
工作原理的人。如果我能更好地理解这一点,我就会马上意识到这个问题。
我有一些使用包的代码。该包提供了一些具有可选参数的命令。我为这些参数提供了一些文本,但我希望能够编译我的文档的替代版本,其中隐藏了该文本。
因此,我尝试定义某种布尔标志,当它设置为 true 时,结果就像这些参数为空一样。不幸的是,我尝试过的所有方法似乎都无法实现这一点。
我将在下面附上一个 MWE,说明我尝试过的几种方法,并说明为什么结果不令人满意。我不知道还能尝试什么,所以任何帮助都将不胜感激!
\documentclass{article}
\usepackage{etoolbox}
% I want a flag that, if true, will cause certain strings to be hidden, as if
% they were not in the document at all. Below I try a couple ways of defining
% such a flag.
\newif\ifhidestuff
\hidestufftrue
% \hidestufffalse
\newtoggle{hidestuff}
\toggletrue{hidestuff}
% \togglefalse{hidestuff}
% Below is an example of a command that behaves differently according as one
% of its arguments is empty
\newcommand{\withoptarg}[2]{#1{%
\ifx#2\else{ and #2}\fi}}
\newcommand\hidden[1]{\ifhidestuff%
% not sure what to put here
\else
#1
\fi}
\begin{document}
% The \hidden command seems to work fine on its own
The stuff between the quotes might be hidden: ``\hidden{stuff}''
% Now let's see if we can hide an optional argument to the \withoptarg command
This is the output I want when the flag is true: \withoptarg{this}{}
This is the output I want when the flag is false: \withoptarg{this}{that}
With my first approach, I instead get: \withoptarg{this}{\iftoggle{hidestuff}{}{that}}
With my second approach, I also get: \withoptarg{this}{\hidden{that}}
\end{document}
这是我得到的输出:
更新:有问题的包是moderncv
。我编写代码\withoptarg
来模仿 的实现方式\cventry
,以尽可能简化 MWE。但是——我的错——我使用的是过时的包版本。下一个版本包含作者的评论,它“纠正了与 中检查空参数的代码相关的几个错误\cventry
”。事实上,升级到 的最新版本moderncv
解决了我的问题。
如果我对\ifx
工作原理的理解更深入一些,我可能已经识别出这个错误并意识到发生了什么。无论如何,我至少在这里学到了一些知识。
我将删除这个问题,但我会将其保留一段时间,只是为了感谢那些写信帮助的人的帮助。
答案1
您可以定义一个宏,根据-switch 传递\@gobble
或。 您可以定义一个宏,根据-switch传递或。\@firstofone
\if..
\@firstoftwo
\@scondoftwo
\if..
如果您希望在文本不可见的情况下保留水平/垂直空间,则可以使用\@firstoftwo
/变量在和或类似的之间切换。\@scondoftwo
\phantom{\mbox{text}}
\@firstofone{\mbox{text}}
\documentclass{article}
\newif\ifhidestuff
\newcommand\StuffToHide{%
\csname @\ifhidestuff gobbl\else firstofon\fi e\endcsname
}%
\newcommand\StuffToShowOrHide{%
\csname @\ifhidestuff second\else first\fi oftwo\endcsname
}%
\newcommand\withoptarg[2][Optional Default]{%
This is the optional argument in parentheses: (#1)\\
This is the mandatory argument in parentheses: (#2)
}%
\parindent=0pt \parskip=\medskipamount
\begin{document}
\verb|\hidestufffalse|:\hidestufffalse
\withoptarg{Mandatory}
\withoptarg[\StuffToHide{Optional User-Provided}]{Mandatory}
\StuffToShowOrHide{\withoptarg[Optional User-Provided]}{\withoptarg}{Mandatory}
\StuffToShowOrHide{\withoptarg[Optional User-Provided]}{\withoptarg[]}{Mandatory}
\withoptarg[\csname\StuffToShowOrHide{@firstofone}{phantom}\endcsname{\mbox{Optional User-Provided}}]{Mandatory}
\medskip\hrule
\verb|\hidestufftrue|:\hidestufftrue
\withoptarg{Mandatory}
\withoptarg[\StuffToHide{Optional User-Provided}]{Mandatory}
\StuffToShowOrHide{\withoptarg[Optional User-Provided]}{\withoptarg}{Mandatory}
\StuffToShowOrHide{\withoptarg[Optional User-Provided]}{\withoptarg[]}{Mandatory}
\withoptarg[\csname\StuffToShowOrHide{@firstofone}{phantom}\endcsname{\mbox{Optional User-Provided}}]{Mandatory}
\end{document}
如果你想检查一个参数是否为空,即参数中根本不包含任何标记,我可以提供两种测试变体。
一种变体\CheckWhetherNullVanilla
速度较慢,但不需要任何 TeX 扩展。它基于使用\string
和应用括号黑客,同时确定}
空参数后面的结束符是否被字符串化,或者{
非空参数的第一个标记(不是开头的)是否被字符串化,或者{
非空参数的第一个标记(开头的)是否被字符串化。
另一种变体\CheckWhetherNulleTeX
速度更快,但需要 eTeX 扩展,因为它基于\detokenize
,在这种情况下⟨一般文本⟩通过宏参数提供的为空表示根本不提供任何标记,否则将提供类别与类别不同的字符标记,$
以便\ifcat
可以使用该类别来完成这个技巧。(\ifcat$\detokenize{...}$...\else...\fi
您也可以使用\if\relax\detokenize{...}\relax...\else...\fi
,但这依赖于\relax
不被重新定义来提供两个后续的相等标记,而我看到用户在本地范围内做各种奇怪的事情。)
除此之外,还有一个测试用于检查一个参数是否为空,即为空或仅包含明确的空格标记,,\CheckWhetherBlank
它基于\CheckWhetherNullVanilla
或\CheckWhetherNulleTeX
。
测试寻找标记的存在,但它们不会扩展可扩展标记,因此,例如,\CheckWhetherNulleTeX{\empty}{empty}{not empty}
产生not empty
因为,虽然在扩展时只是消失并且没有产生任何标记,但\empty
它本身是一个标记,因此\CheckWhetherNulleTeX
的第一个参数不是空的,而是由标记 组成\empty
。
评论:
在随后的编码示例中,您会发现\romannumeral
。
乍一看这可能有点令人困惑。
在随后的编码示例中,\romannumeral
它不用于获取任何数字的小写罗马表示,而是(ab?)用于触发大量扩展工作和翻转宏参数工作,以便在需要控制扩展的情况下不需要那么多/长的\expandafter
链。
\romannumeral
通常用于“吞食”形成 TeX-⟨数字⟩-数量,并作为回报,他们提供构成该 TeX 值的表示的字符标记-⟨数字⟩- 数量以小写罗马数字表示。
\romannumeral
在任何情况下都会触发“吞噬”那些形成 TeX-⟨数字⟩-数量。但如果 TeX-⟨数字⟩-quantity 确实有一个非正值,默默地,即,没有错误消息或类似信息,根本没有传递任何令牌作为回报。
除此之外,在搜索属于 TeX-⟨数字⟩-数量,可扩展令牌的扩展不受抑制。
因此,你可以(滥用)使用\romannumeral
来搜索属于 TeX-⟨数字⟩-quantity 让 (La)TeX 做大量的扩展工作和翻转宏参数工作,只要确保最终 TeX-⟨数字⟩- 发现数量的值不是正值。
在随后的编码示例中,作为“TeX-⟨数字⟩-其值不是正数”时使用了\chardef
-token 。\UD@stopromannumeral
这样,宏的结果将在触发两个扩展步骤后传递,例如,在两次“命中”之后\expandafter
:
第一个传递顶层扩展,其第一个标记是\romannumeral
。
第二个触发执行\romannumeral
例程,进而触发所有后续扩展步骤,直到出现结果。
如果您希望在表格环境/对齐等方面也能顺利运行,请确保用户提供的参数(可能包含类似 的内容)&
嵌套在花括号中,直到最后一个扩展步骤提供结果。否则,&
用户提供的参数中的 可能会在错误的时间点被用作表格单元格结尾的标记。
\documentclass[a4paper]{article}
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@stopromannumeral,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%% In the following code \romannumeral is not used for
%%-----------------------------------------------------------------------------
%% Check whether argument is empty, slow, but no eTeX-extensions or
%% the like required:
%%.............................................................................
%% \CheckWhetherNullVanilla{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\CheckWhetherNullVanilla[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty, based on \detokenize, thus eTeX-extensions
%% are required:
%%.............................................................................
%% \CheckWhetherNulleTeX{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
\newcommand\CheckWhetherNulleTeX[1]{%
\romannumeral\ifcat$\detokenize{#1}$%
\expandafter\expandafter\expandafter\UD@stopromannumeral
\expandafter\UD@firstoftwo\else
\expandafter\expandafter\expandafter\UD@stopromannumeral
\expandafter\UD@secondoftwo\fi
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only explicit space-tokens):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%% "fetching" _un_delimited arguments: --
%% \CheckWhetherBlank{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked is blank>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not blank>}%
%% Instead of \CheckWhetherNulllVanilla you can as well use
%% \CheckWhetherNullleTeX
\newcommand\CheckWhetherBlank[1]{%
\romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
\expandafter\CheckWhetherNullVanilla\expandafter{\UD@firstoftwo#1{}{}}%
}%
\makeatother
\parindent=0pt \parskip=\medskipamount
\begin{document}
\verb|\CheckWhetherNullVanilla{}{empty}{not empty}|:\\
\CheckWhetherNullVanilla{}{empty}{not empty}
\verb|\CheckWhetherNulleTeX{}{empty}{not empty}|:\\
\CheckWhetherNulleTeX{}{empty}{not empty}
\verb|\CheckWhetherBlank{}{blank}{not blank}|:\\
\CheckWhetherBlank{}{blank}{not blank}
\medskip\hrule
\verb|\CheckWhetherNullVanilla{ }{empty}{not empty}|:\\
\CheckWhetherNullVanilla{ }{empty}{not empty}
\verb|\CheckWhetherNulleTeX{ }{empty}{not empty}|:\\
\CheckWhetherNulleTeX{ }{empty}{not empty}
\verb|\CheckWhetherBlank{ }{blank}{not blank}|:\\
\CheckWhetherBlank{ }{blank}{not blank}
\medskip\hrule
\verb|\CheckWhetherNullVanilla{{some}thing}{empty}{not empty}|:\\
\CheckWhetherNullVanilla{{some}thing}{empty}{not empty}
\verb|\CheckWhetherNulleTeX{{some}thing}{empty}{not empty}|:\\
\CheckWhetherNulleTeX{{some}thing}{empty}{not empty}
\verb|\CheckWhetherBlank{{some}thing}{blank}{not blank}|:\\
\CheckWhetherBlank{{some}thing}{blank}{not blank}
\medskip\hrule
\verb|\CheckWhetherNullVanilla{something}{empty}{not empty}|:\\
\CheckWhetherNullVanilla{something}{empty}{not empty}
\verb|\CheckWhetherNulleTeX{something}{empty}{not empty}|:\\
\CheckWhetherNulleTeX{something}{empty}{not empty}
\verb|\CheckWhetherBlank{something}{blank}{not blank}|:\\
\CheckWhetherBlank{something}{blank}{not blank}
\medskip\hrule
\verb|\CheckWhetherNullVanilla{\empty}{empty}{not empty}|:\\
\CheckWhetherNullVanilla{\empty}{empty}{not empty}
\verb|\CheckWhetherNulleTeX{\empty}{empty}{not empty}|:\\
\CheckWhetherNulleTeX{\empty}{empty}{not empty}
\verb|\CheckWhetherBlank{\empty}{blank}{not blank}|:\\
\CheckWhetherBlank{\empty}{blank}{not blank}
\end{document}