出于实际原因,我想用有点冗长的
\ifcsname some_variable\endcsname
\fi
通过组合
\def\customif#1{\ifcsname #1\endcsname}
\customif{some_variable}
\fi
即使 some_variable 未定义,第一个方法也能按预期工作,但该\customif
方法会产生一些意想不到的结果。
我知道我肯定遗漏了一些东西,但我不明白为什么下面的代码无法编译。
\documentclass{article}
\def\customif#1{\ifcsname #1\endcsname}
\begin{document}
\expandafter\def\csname some_variable\endcsname{}
\ifcsname some_variable\endcsname
This works.
\fi
\customif{some_variable}
So does this.
\fi
\ifcsname other_variable\endcsname
\ifcsname other_variable\endcsname
This compiles and does not print (as expected).
\fi
\fi
\customif{other_variable}
This also compiles and does not print.
\fi
\customif{other_variable}
\customif{other_variable}
This fails.
\fi
\fi
\customif{other_variable}
\else
\customif{other_variable}
\else
On the other hand, this compiles and prints.
\fi
\fi
\end{document}
使用 pdflatex 编译我得到
! Extra \fi.
l.30 \fi
答案1
在你的例子中
\customif{other_variable}
\customif{other_variable}
This fails.
\fi
\fi
内部\customif
没有展开,因此\ifcsname
其替换文本中的 不可见。因此,第一个与外部展开的结果\fi
配对,而下一个则单独悬空。\ifcsname
\customif
更详细地说:代码在扩展后\customif{other_variable}
变为
\ifcsname other_variable\endcsname
\customif{other_variable}
This fails.
\fi
\fi
测试结果为 false,因此第一的 \fi
被跳过。
你可以用不同的方式来做:
\def\custom#1{TT\fi\ifcsname #1\endcsname}
然后将其称为
\if\custom{some_variable}
<true text>
\else
<false text>
\fi
由于 ,它也可以嵌套使用\if
。(必要时请不要忘记保护行尾。)
如果你担心悬垂的首字母\fi
(但在扩展后会立即消失),你可以这样做
\def\custom#1{T\expandafter X\expandafter X\fi\ifcsname #1\endcsname}
另一种方法是使用“支撑枝”:
\makeatletter
\newcommand*\vardefTF[1]{%
\ifcsname #1\endcsname
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\makeatother
被称为
\vardefTF{some_var}{True}{False}
这样做的缺点是真假分支会被作为参数读入,但嵌套时问题较少。
这expl3
是预定义的,所以
\usepackage{expl3}
\ExplSyntaxOn
\cs_set_eq:NN \vardefTF \cs_if_exist:cTF
\ExplSyntaxOff
会提供\vardefTF
像以前一样的行为。
Bruno Le Floch 提出了一个不同的想法:
\def\custom#1{%
T\ifcsname#1\endcsname
\expandafter T\else\expandafter F\fi
}
如果代码
\if\custom{some_variable}<True>\else<False>\fi
在跳过的条件分支内,将\if
通过 和 进行平衡\else
(通常为可选)\fi
。另一方面,当\if
不跳过和扩展时,当\ifcsname
返回 true 时,\if
将找到TT
,否则将找到TF
,并相应地选择以下分支。
这样做的好处是可以\if
在前面加上\unless
。
(这个老TT\fi
把戏在它出现之前就已经诞生了\unless
。)
答案2
TT
我使用的方法与这种情况不同。我定义了\is...
实现测试的宏,它比实际需要的参数多一个。这个(忽略的)参数是\iftrue
使用宏时使用的。我们的示例如下:
\def\isdefined#1#2{\ifcsname #1\endcsname}
和用法:
\isdefined{some_text}\iftrue
<true text>
\else
<false text>
\fi
请注意,不需要将其放在%
第一行的末尾。
答案3
另一种可能性是使用 LaTeX 样式的条件。例如,在下面的代码中,我们定义了一个命令\CustomIfDef
,with 应按如下方式使用
\CustomIfDef{<cs-name>}{<if-defined-code>}{<if-undefined-code>}
并且可以毫无问题地嵌套。但请注意,在嵌套的情况下,它比 @egreg 方法效率低,因为各种情况的代码需要在级别之间作为参数进行复制。
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
% End of standard header. What follows pertains to the problem at hand.
\makeatletter
\newcommand*\CustomIfDef[1]{%
\ifcsname #1\endcsname
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\newcommand*\CustomDefEmpty[1]{\@namedef{#1}{}}
\makeatother
\nofiles
\begin{document}
\CustomDefEmpty{Foo_bar_123.$}
\CustomIfDef{Pluto!}{
\typeout{==> Outer branch true.}
}{
\typeout{==> Outer branch false.}
\CustomIfDef{Foo_bar_123.$}{
\typeout{----> Mid-level branch true.}
\CustomIfDef{relax}{
\typeout{......> Inner branch: \relax is defined.}
}{
\typeout{......> Inner branch: \relax is NOT defined.}
}
}{
\typeout{----> Mid-level branch false.}
}
}
\end{document}