是否存在不受 \uppercase/\lowercase 影响且不能用 \outer 进行(重新)定义的标记?

是否存在不受 \uppercase/\lowercase 影响且不能用 \outer 进行(重新)定义的标记?

\uppercase是否存在不受/影响\lowercase且不能(重新)定义的标记\outer

(如果是这样,我想将它用作可以在\uppercase/内部使用的内容的参数分隔符\lowercase。)

答案1

据我所知,没有这样的标记:

每个标记至少属于以下两类标记中的一类 - 活动角色标记同时属于这两类:

  1. 控制序列。(控制字标记、控制符号标记、活动字符标记。)所有控制序列都可以根据 进行(重新)定义\outer
  2. 显式字符标记。每个显式字符标记都会受到\uppercase/的影响\lowercase(前提是其\uccode/\lccode已进行相应设置)。

在许多情况下,您可以完全避免使用带分隔符的参数,而是使用不带分隔符的参数并检查其是否为空。

例如,为了从大括号平衡的标记列表中提取第一个无限参数,我经常使用如下方法:

%%   \romannumeral\UD@ExtractFirstArgLoop{<argument>\UD@SelDOm}%
%%   yields <argument>'s 1st undlimited argument.
%%   <argument> must not be blank, i.e., must neither be empty nor consist
%%   only of explicit character tokens of catcode 10 and charcode 32.
%%
%%   \UD@SelDOm must not be defined in terms of \outer !
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
  \long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
  {\expandafter\z@\@secondoftwo{}#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%

\UD@CheckWhetherNull定义为

%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<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\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\z@\@secondoftwo}%
  {\expandafter\z@\@firstoftwo}%
}%

\newcommand\UD@CheckWhetherNull[1]{%
  \romanumeral\ifcat$\detokenize{#1}$%
  \expandafter\expandafter\expandafter\z@\expandafter\@firstoftwo\else
  \expandafter\expandafter\expandafter\z@\expandafter\@secondoftwo\fi
}%

。 )

通过此您可以获得:

\romannumeral\UD@ExtractFirstArgLoop{{A}{B}CDE\UD@SelDOm}%A

为了减少删除除第一个非分隔参数之外的所有内容所需的迭代次数,我使用了以 分隔的参数\UD@SelDOm。(所需的迭代次数为:“\UD@SelDOm参数中未嵌套在括号中的次数”+1)。

如果您不喜欢 -delimiter \UD@SelDOm(因为它可能根据 进行定义)\outer,那么您可以按如下方式省去它(但需要更多次迭代才能获得结果),所需的迭代次数为:“参数中未嵌套在括号中的未分隔参数的数量”+1:

% Syntax now is: \romannumeral\UD@ExtractFirstArgLoop{<argument>{}}%
\newcommand\UD@GrabFirst[2]{{#1}}%
\renewcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
  {\expandafter\z@\@secondoftwo{}#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@GrabFirst#1}}%
}%

例如,

\romannumeral\UD@ExtractFirstArgLoop{{A}{BC}DE{}}%A

在很多情况下,您可以做类似的事情以完全避免分隔参数。


有时人们使用处理相关参数的宏标记作为参数分隔符,例如\def\macro#1\macro{...}。这是否可行/合理取决于是否\macro可以嵌套在其自己的参数中并因此错误地匹配分隔符,或者是否\let\macrob=\macro \outer\def\macro...会发生类似的情况。



既然您已经在思考如何使宏参数安全的问题,我想指出除了\outer\def...and \uppercase/之外还有其他陷阱\lowercase

  1. 例如,基于分隔参数的宏机制是否应该在表格环境/对齐内工作,该机制应处理用户给出的(几乎)任意参数。

    例如,假设一个宏\grabdelimited处理一个分隔参数,用户使用它来收集和解析字符B&

    \documentclass{article}
    \def\grabdelimited#1\delimiter{Arguments grabbed: \detokenize{#1}}%
    \begin{document}
    \grabdelimited B&\delimiter
    
    \makeatletter
    \begin{tabular}{|l|l|}
    %A&\relax\grabdelimited B&\delimiter\\
    A&\relax\expandafter\@firstofone\expandafter{\grabdelimited B&\delimiter}
    \end{tabular}
    \end{document}
    

    表格环境中的第一个/注释行会产生错误,而第二行不会,因为这里&属于分隔参数的内容隐藏在花括号内 \@firstofone

  2. 另一个问题可能是将不平衡的 \if.../ \else/\fi作为宏参数传递,这可能会错误地匹配一些\if.../\else处理这些参数的宏定义中出现的

    \csname与不平衡的/相同\endcsname

答案2

扩展评论:

如果您真的担心参数结束标记可能会受到\uppercase/ 的影响\lowercase,那么您只需确保它不会出现在顶层,以便它永远不会被\uppercase或看到\lowercase。这可以通过确保它仅插入到扩展无法在其间停止的上下文中来实现,例如,通过使用\romannumeral扩展上下文。以下设置了一个使用字符作为参数结束标记的宏,但由于它进一步扩展,该标记永远不会留在输入流中,因此它可能会受到或 的D影响:\lowercase\uppercase

% first insert a \romannumeral such that the following is expanded as far as possible
\newcommand*\mymacro{\romannumeral\mymacro@a}
% only after \romannumeral has started input the delimiter
\newcommand\mymacro@a[1]{\mymacro@b #1D}

然后\mymacro@b可以以可扩展的方式处理参数并用作D分隔符。你可以\romannumeral用 结束扩展上下文\z@。当然,你仍然可以扩展\mymacro一次,然后\mymacro@a用扩展\expandafter而不开始\romannumeral,这样D可能会受到影响\lowercase(用\expandafter\expandafter\expandafter\lowercase\expandafter\expandafter\expandafter{\expandafter\expandafter\mymacro{}} ) 的影响,但至少现在这必须是出于恶意而创建的。

您永远无法保护自己免受\outer重新定义的影响,但是重新定义其他人代码内部的人似乎\outer不想拥有有效的代码,所以也许这不是您需要保护自己的情况。

相关内容