将 xparse 可选 token 参数从外部命令传递给内部命令

将 xparse 可选 token 参数从外部命令传递给内部命令

我有一个文档命令,它接受几个可选的 token 参数,我想实现另一个文档命令,它接受相同的可选 token 参数并将它们正确地传递给第一个命令。我在这个网站上搜索了答案,虽然有很多关于将可选参数从一个命令传递到另一个命令的问题,但没有一个(我发现的)能解决我的 xparse 用例令牌(即t类型)可选参数(例如,它们是 for\newcommand而不是\NewDocumentCommand)。

以下是 MWE:

\documentclass{article}
\usepackage[normalem]{ulem}
\usepackage{xparse}

\NewDocumentCommand\decorate{t^ t+ t! m}{%
    \IfBooleanTF{#1}{%
        \IfBooleanTF{#2}%
            {\emph{\uline{#4}}}%
            {\IfBooleanTF{#3}{\emph\sout{#4}}}{\emph{#4}}%
    }%
    {\IfBooleanTF{#2}%
        {\uline{#4}}%
        {\IfBooleanTF{#3}{\sout{#4}}{#4}}%
    }%
}

% works but re-implements \decorate
% \NewDocumentCommand\dsection{t^ t+ t! m}{%
%   \IfBooleanTF{#1}{%
%       \IfBooleanTF{#2}%
%           {\section{\emph{\uline{#4}}}}%
%           {\IfBooleanTF{#3}{\section{\emph\sout{#4}}}}{\section{\emph{#4}}}%
%   }%
%   {\IfBooleanTF{#2}%
%       {\section{\uline{#4}}}%
%       {\IfBooleanTF{#3}{\section{\sout{#4}}}{\section{#4}}}%
%   }%
%   % additional commands not related to \decorate
% }

% % works but requires the same if/else branching as in \decorate
% \NewDocumentCommand\dsection{t^ t+ t! m}{%
%   \IfBooleanTF{#1}{%
%       \IfBooleanTF{#2}%
%           {\section{\decorate^+{#4}}}%
%           {\IfBooleanTF{#3}{\section{\decorate^!{#4}}}}{\section{\decorate^{#4}}}%
%   }%
%   {\IfBooleanTF{#2}%
%       {\section{\decorate+{#4}}}%
%       {\IfBooleanTF{#3}{\section{\decorate!{#4}}}{\section{#4}}}%
%   }%
%   % additional commands not related to \decorate
% }

% does not work, but would have the desired property of passing the token arguments to \decorate
\NewDocumentCommand\dsection{t^ t+ t! m}{%
    \section{\decorate \IfBooleanT{#1}{#1}\IfBooleanT{#2}{#2} \IfBooleanT{#3}{#3}{#4}}
    %\section{\decorate#1#2#3{#4}} % also does not work
    %\section{\decorate \IfBooleanT{#1}{^}\IfBooleanT{#2}{+} \IfBooleanT{#3}{!}{#4}} % causes "missing $ inserted" error
    % additional commands not related to \decorate
}

\begin{document}
\dsection^{Caret section}
\decorate^{Caret text}

\dsection+{Plus section}
\decorate+{plus text}

\dsection!{Bang section}
\decorate!{bang text}
\end{document}

我想调用\decoratewithin\dsection并正确地将 token 参数传递给它,但我的所有尝试都失败了。在上面未注释的尝试中,“修饰”未应用(即,\decorate实际上调用时没有任何 token 参数)。

如注释掉的定义之一所示,\dsection我可以\decorate通过一次硬编码一个标记参数来调用其定义,但这需要我重新实现IfBooleanTF中的逻辑\decorate。如何\dsection使用单次调用来实现\decorate,同时正确地将可选的标记参数传递给它?

答案1

user202729 已经提供了一段代码您可以在其中看到解决方案是什么样的。

因此,让我们集中讨论一下为什么你的代码不起作用:

xparse 的一些参数说明符表明对参数进行了预处理。

因此,您需要区分从标记流中抓取时形成参数的标记集与形成预处理参数结果的标记集。

在从相应定义的替换文本的标记流#n#n#1#2#3#4#5#6#7或)中抓取后立即进行预处理的参数并不引用形成该参数的标记集,但引用形成预处理该参数的结果的标记集。#8#9\NewDocumentCommand

xparse 包的手册中关于t-argument-specifier 有如下描述:

t可选的 ⟨token⟩,\BooleanTrue如果 ⟨token⟩ 存在,则会产生一个值,\BooleanFalse否则会返回一个值。以 ⟨token⟩ 的形式给出t

这意味着t从标记流中抓取标记后,会对形成 -type-argument 的标记进行预处理,以便-definition 的替换文本(或或或或或或或或)#n中使用的对应标记要么是标记,要么是标记。\NewDocumentCommand#1#2#3#4#5#6#7#8#9\BooleanTrue\BooleanFalse

因此t-type-arguments 只能作为参数传递给其他宏,这些宏“期望”相应的参数由 token\BooleanTrue或 token组成\BooleanFalse

这就是 user202729 所做的。


您不起作用的尝试是:

\NewDocumentCommand\dsection{t^ t+ t! m}{%
    \section{\decorate \IfBooleanT{#1}{#1}\IfBooleanT{#2}{#2} \IfBooleanT{#3}{#3}{#4}}
    %\section{\decorate#1#2#3{#4}} % also does not work
    %\section{\decorate \IfBooleanT{#1}{^}\IfBooleanT{#2}{+} \IfBooleanT{#3}{!}{#4}} % causes "missing $ inserted" error
    % additional commands not related to \decorate
}

尝试

\section{\decorate \IfBooleanT{#1}{#1}\IfBooleanT{#2}{#2} \IfBooleanT{#3}{#3}{#4}}

不起作用,因为没有将标记^、标记+或标记!传递给,\decorate因此\decorate没有提供可选参数,并且\IfBooleanT标记后面的单个标记\decorate被视为 的\decorate-type m-argument。

尝试

\section{\decorate#1#2#3{#4}}

不起作用,因为不是标记^, +,而是来自预处理的三个可选-类型参数!的三个标记\BooleanTrue/的组合,并通过 表示,放在标记后面,这样TeX 找不到可能被用作可选参数的标记, ,但会找到其中一个标记/ ,它被用作 的 非可选-类型参数。\BooleanFalse\dsectiont#1#2#3\decorate\decorate^+!\BooleanTrue\BooleanFalse\decoratem

尝试

\section{\decorate \IfBooleanT{#1}{^}\IfBooleanT{#2}{+} \IfBooleanT{#3} {!}{#4}}

不起作用,因为\decorate后面没有标记^+!可能被视为可选参数,但后面有一个标记\IfBooleanT,该标记被视为 \decorate非可选m类型参数。

但这次尝试似乎非常接近您可能需要的:

不应该将标记附加\IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}在标记的正后方,\decorate 而应该将所有这些标记完全展开后的结果附加在标记的正后方\decorate

e本答案的末尾显示了如何使用 expl3/LaTeX3 的 -expansion 来实现这一点。


这一切的要点是:

使用 xparse-argument,根据相应的\NewDocumentCommand-argument-specifier 进行预处理,#n#n为 或#1#2#3#4#5#6或)在-definition 的替换文本中指的是形成预处理该参数的结果的一组标记#7#8#9\NewDocumentCommand

有关预处理 xparse-arguments 的可能结果的信息可以在 xparser 包的手册中找到。


如果你喜欢复杂的,你可以使用 expl3 的-expansione来创建一个^//序列并将其附加到标记后面:+!\decorate

\documentclass{article}
\usepackage[normalem]{ulem}
\usepackage{xparse}

\newcommand\foo[1]{#1}% foo is abbreviation for "first of one".

\NewDocumentCommand\decorate{t^ t+ t! m}{%
  \IfBooleanTF{#1}{\emph}{\foo}%
  {%
    \IfBooleanTF{#2}{\uline}{\foo}%
    {%
      \IfBooleanTF{#3}{\sout}{\foo}%
      {#4}%
    }%
  }%
}

\ExplSyntaxOn
\NewDocumentCommand\dsection{t^ t+ t! m}{%
    \section{\exp_last_unbraced:Ne \decorate {\IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}} {#4}}%
    % additional commands not related to \decorate and where #1, #2 and #3 each denote one of the tokens
    % \BooleanTrue or \BooleanFalse and #4 denotes the m-type argument.
}
\ExplSyntaxOff

%% With a recent LaTeX release you don't need expl3-syntax but can use \ExpandArgs :
%\newcommand\tot[2]{#1#2}% tot is abbreviation for "two of two".
%\NewDocumentCommand\dsection{t^ t+ t! m}{%
%    \section{\ExpandArgs{ne}\tot{\decorate}{\IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}}{#4}}%
%    % additional commands not related to \decorate and where #1, #2 and #3 each denote one of the tokens
%    % \BooleanTrue or \BooleanFalse and #4 denotes the m-type argument.
%}



\begin{document}

\dsection{Section}
\decorate{Text}

\dsection^{Caret section}
\decorate^{Caret text}

\dsection+{Plus section}
\decorate+{plus text}

\dsection!{Bang section}
\decorate!{bang text}

\dsection^+!{Caret Plus Bang section}
\decorate^+!{caret plus bang text}

\end{document}

在此处输入图片描述


如果你想知道这\exp_last_unbraced:Ne是关于什么的:

\exp_last_unbraced:Ne接受两个参数:

对于第一个参数,预处理是根据 -type-argument-specifier 所表示的规则进行的N
对于第二个参数,预处理是根据e-type-argument-specifier 所表示的规则进行的。
构成预处理第一个参数 ( N-type-argument) 的结果的标记被插入到标记流中,后面跟着构成预处理第二个参数 ( e-type-argument) 的结果的标记。 -
type N-specifier 表示参数由单个标记组成,该标记将被插入到标记流中而无需任何进一步的预处理。 -
type e-specifier 表示参数可能由多个标记组成,并且\expanded{...}将应用于形成参数的标记,从而产生形成参数的标记的完全展开。

有许多命令类似于\exp_args:...\exp_last_unbraced:...具有非常相似的名称,只是参数说明符不同。

所有这些带有参数的命令(例如N-type)的预处理结果都由单个(控制序列)标记组成,形成预处理结果的单个标记将附加到标记流中,而无需嵌套在花括号之间{...}

对于预处理结果可能由多个标记组成的参数(如e-type),形成预处理结果的标记{...}在附加到标记流时通常嵌套在一对匹配的花括号之间。

last_unbraced表示尽管通常将由几个标记组成的预处理结果包装在一对匹配的花括号之间{...},但预处理最后一个参数的结果,即预处理 e 类型参数的结果,应附加到标记流中,而不嵌套在花括号之间。

答案2

“将接口与内部实现分开”。这是常见的建议。

\documentclass{article}
\usepackage[normalem]{ulem}
\usepackage{xparse}

\ExplSyntaxOn

\cs_new_protected:Nn \__null_decorate:nnnn {
    \IfBooleanTF{#1}{
        \IfBooleanTF{#2}
            {\emph{\uline{#4}}}
            {\IfBooleanTF{#3}{\emph\sout{#4}}}{\emph{#4}}
    }
    {\IfBooleanTF{#2}
        {\uline{#4}}
        {\IfBooleanTF{#3}{\sout{#4}}{#4}}
    }
}

\NewDocumentCommand\decorate{t^ t+ t! m}{
    \__null_decorate:nnnn {#1}{#2}{#3}{#4}
}

\NewDocumentCommand\dsection{t^ t+ t! m}{%
    \section{ \__null_decorate:nnnn {#1}{#2}{#3}{#4} }
}

\ExplSyntaxOff


\begin{document}
\dsection^{Caret section}
\decorate^{Caret text}

\dsection+{Plus section}
\decorate+{plus text}

\dsection!{Bang section}
\decorate!{bang text}
\end{document}

回想起来,这个解决方案应该是显而易见的。(__null以 OP 的用户名命名)

相关内容