在宏中使用时命令 \ifcsname 无法正确扩展

在宏中使用时命令 \ifcsname 无法正确扩展

出于实际原因,我想用有点冗长的

\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}

相关内容