在非 etex 情况下,什么是 ifcsname 的合适替代品?

在非 etex 情况下,什么是 ifcsname 的合适替代品?

来自pgfkeys.code.tex文件:

% This is useful:

\def\pgfkeys@ifcsname#1\endcsname#2\else#3\fi{\expandafter\ifx\csname#1\endcsnam
e\relax#3\else#2\fi}%
\ifx\eTeXrevision\undefined%
\else%
  \expandafter\let\expandafter\pgfkeys@ifcsname\csname ifcsname\endcsname%
\fi

该命令\pgfkeys@ifcsname在内部用于检查密钥是否存在。如果etex存在,则只需执行\ifcsname。如果不存在,则这是一个后备方案,在使用它的情况下,该方案被认为足够好。

但是,它不能很好地与嵌套条件配合使用。有时它通过另一个命令使用:

\long\def\pgfkeysifdefined#1#2#3{\pgfkeys@ifcsname pgfk@#1\endcsname#2\else#3\fi}

这里的问题是,如果#2#3包含另一个条件,则因为\pgfkeys@ifcsname使用宏参数匹配而不是条件匹配,所以\else\fi中的#2#3可以匹配而不是给定的或。添加括号不起作用,因为那会增加一组额外的括号。

现在,我打算在 PGF 错误列表中报告此问题,但我认为我应该先尝试提出替代方案。

我想到的是:

\def\pgfkeys@ifcsname#1\endcsname{\ifx\csname#1\endcsname\relax\expandafter\iffalse\else\expandafter\iftrue\fi}

所以我的问题是:这款产品是否还存在原来没有遇到的问题?

(因此,使其\csname undefinedcommand\endcsname成为\undefinedcommand并不\relax反对新定义,因为它在原始定义中已经存在。)

答案1

PGF代码在很多方面都是错误的。

  1. \else当宏嵌套在其他条件中使用时,用and限定参数\fi没有帮助。

  2. 如果参数是,则宏会给出不同的结果relax,取决于 e-TeX 是否可用。

我会使用通常的间接方法:

\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\def\@pgfkeys@csname#1\endcsname{%
  TT\fi
  \expandafter\ifx\csname#1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  \iffalse\iftrue
  }

用作

\if\@pgfkeys@csname something\endcsname
   true case%
\else
   false case%
\fi

定义在嵌套条件中正确运行的“宏条件”的方法。另一种方法是使用 LaTeX 样式的参数条件,使用通常的\@firstoftwoand \@secondoftwo,基本上与

\@ifundefined{macro}
  {<not defined or \relax case>}
  {<defined and not \relax case>}

很容易“反转”这个来获得\@ifdefined宏,只需在 的定义中交换\@firstoftwo和 即可。\@secondoftwo\@ifundefined

是否定义了 TeX 宏?

答案2

需要注意的是,无论 的定义如何\pgfkeys@ifcsname,它都不能安全地在另一个 TeX 条件中使用:尝试这样做\if[...]\pgfkeys@ifcsname...\endcsname...\fi...\fi会导致第一个\fi关闭 ,第二个\fi关闭\if,而第二个\fi则未使用。但是, 的当前定义\pgfkeys@ifcsname甚至不允许在其分支中使用其他 TeX 条件,这个问题可以修复。


首先说一句小话:你可能忘了在定义中把 放在\expandafter前面,以扩展。让我们把它放回去:\ifx\csname

\def\pgfkeys@ifcsname#1\endcsname
  {%
    \expandafter\ifx\csname#1\endcsname\relax
      \expandafter\iffalse
    \else
      \expandafter\iftrue
    \fi
  }

我选择的布局符合预期:如果构造\csname#1\endcsname结果为\relax,则我们要调用\iffalse并采取随后的错误分支(因为命令#1未定义),否则调用\iftrue。不幸的是,这失败了。

  • 如果命令不存在,则\ifx测试为真,TeX 看到\expandafter,扩展\else,这会导致它跳转到匹配的\fi,但是......没有匹配的\fi,因为 TeX 将其视为\iftrue\fi嵌套条件。
  • 如果命令存在,则\ifx测试为假并尝试跳转到以下\fi。但它看到一个\iffalse条件,一个\iftrue\fi匹配的\iftrue,并且必须还有两个来\fi关闭\if...s 。

有几种方法可以避免混淆条件:

\def\pgfkeys@ifcsname#1\endcsname
  {%
    \expandafter\ifx\csname#1\endcsname\relax
      \expandafter\@firstoftwo
    \else
      \expandafter\@secondoftwo
    \fi
    \iffalse\iftrue
  }

和定义为 LaTeX 中的和。\@firstoftwo或者\@secondoftwo\long\def\@firstoftwo#1#2{#1}\long\def\@secondoftwo#1#2{#2}

\def\pgfkeys@ifcsname#1\endcsname
  {%
    \csname
      \expandafter\ifx\csname#1\endcsname\relax
        iffalse%
      \else
        iftrue%
      \fi
    \endcsname
  }

为了更加强大,甚至可以避免使用以\endcsname辅助函数为代价来分隔的参数:

\def\pgfkeys@ifcsname{\expandafter\pgfkeys@ifcsname@\csname}
\long\def\pgfkeys@ifcsname@#1%
  {%
    \csname
      \ifx#1\relax
        iffalse%
      \else
        iftrue%
      \fi
    \endcsname
  }

这里我决定把它做\pgfkeys@ifcsname@长,以防测试的控制序列恰好是\par

相关内容