是否有带有可选参数的 `\NewDocumentCommand` 或 `\newcommand` 的 `\edef` 版本?

是否有带有可选参数的 `\NewDocumentCommand` 或 `\newcommand` 的 `\edef` 版本?

在一个\foreach循环中定义一组宏,其中可选参数(示例代码中未显示)。为此,我使用\NewDocumentCommand,但\newcommand也可以工作。

\edef但是我如何在这些定义中获得优势呢?如果没有\edef能力,我看不出有什么办法可以使用循环变量来改变这些定义!

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\begin{document}

\foreach \x in {one, two}{
    \globaldefs=1
    % I would want to use \eNewDocumentCommand here
    \expandafter\NewDocumentCommand\csname\x\endcsname{}{
        \x
    }
}

\expandafter\show\csname one code\endcsname  % seems to show the "real" macro
\one  % fails, because \x was not expanded at definition time

\end{document}

顺便问一下,您如何调试这些新命令?\show\macro是没用的...我注意到宏\csname macro code\endcsname提供了在解析/扩展后查看宏的机会(如果)...

答案1

不确定这有什么用处。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\bunchdefine}{m}
 {
  \clist_map_inline:nn { #1 }
   {
    \exp_args:Nc \NewDocumentCommand { ##1 } { } { ##1 }
   }
 }

\ExplSyntaxOff

\bunchdefine{one,two}

\begin{document}

\one

\two

\end{document}

如您所见,这里没有\x,而只有(由于它在定义主体中所以#1加倍)代表当前项目。#

神秘的\exp_args:Nc意思是:跳过下一个标记,并使用后面的大括号参数形成一个控制序列名称,因此它本质上与

\expandafter\NewDocumentCommand\csname##1\endcsname

但更容易。

答案2

您不仅需要在宏名称中展开\x,还需要在定义中展开。而且您不仅没有需要表格\edef,但在这种情况下,你可能实际上并不需要表单\edef。也就是说,最好\x只展开一次。

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\begin{document}

\foreach \x in {one, two, \today}{
    \globaldefs=1
     \expandafter\NewDocumentCommand\csname\x\expandafter\endcsname\expandafter
    {\expandafter}\expandafter{\x*!@}
}

\one

\two

\csname\today\endcsname\\
= \csname September 5, 2019\endcsname
\end{document}

在此处输入图片描述

如果有人真的想争论充分使用新的 TeX 引擎进行扩展,可以\expanded引入

\expandafter\NewDocumentCommand\csname\x\expandafter\endcsname\expandafter
{\expandafter}\expandafter{\expanded{\x*!@}}

答案3

这就是你要去的地方吗?也就是说,你有来自\newcommand或 的论据,甚至还有\xparse来自 的结果\xdef

\documentclass{article}
\usepackage{tikz}

\newcommand{\createname}[2]{% #1 = name, #2=contents
  \expandafter\xdef\csname #1\endcsname{#2}}

\begin{document}
\foreach \x in {one, two}{\createname{\x}{\x}}

\one

\two
\end{document}

答案4

应用\romannumeral0-expansion,使用\expandafter和交换参数,您可能无需改变参数的值,也根本\globaldefs不需要使用就可以实现您需要的功能:\edef

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand\passfirsttosecond[2]{#2{#1}}%
\newcommand\exchange[2]{#2#1}%
%%---------------------------------------------------------------
%%  \name <token sequence _without_ curly braces>{<tokens that yield the sequence "macroname">}  
%%  -> <token sequence _without_ curly braces>\macroname
%%
%%  Examples: \name{bar} -> \bar
%%            \name\newcommand*{bar}... -> \newcommand*\bar...
%%            \name\outer\global\long\def{bar}...  -> \outer\global\long\def\bar...
%%            \name\string{bar} -> \string\bar
%%            \name\name\let{foo}={bar} 
%%               -> \name\let\foo={bar}
%%               -> \let\foo=\bar
%%
%% The gist of the trick is: Due to #1#-notation \name processes one
%% argument which is delimited by a left-curly-brace.
%% Unlike other argument-delimiters the delimiting left-curly-brace will
%% not be removed but will be left in place when (La)TeX reads the
%% argument.
%% The tokens at the left of the delimiting left-curly-brace are to be
%% prepended after applying \csname...\endcsname to the tokens nested in
%% curly braces.
%%
\csname @ifdefinable\endcsname\name{%
  \long\def\name#1#{\romannumeral0\nameinternal{#1}}%
}%
\newcommand\nameinternal[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%%---------------------------------------------------------------
%% Scratch macro for accumulating calls to \NewDocumentCommand:
%% (Due to tikz's `\foreach` doing each iteration within a
%%  local scope accumulating needs to be done via global
%%  assignments, e.g.,in terms of \g@addto@macro.)
\newcommand\scratchy{}%
%%---------------------------------------------------------------

\def\scratchy{}%
\foreach \x in {one, two}{%
  \name\expandafter{g@addto@macro}\expandafter\scratchy\expandafter{%
    \romannumeral0% <-\romannumeral keeps searching digits, hereby expanding expandable things until
                  % finding something, e.g., a space, that terminates the digit-sequence.
                  % (\romannumeral will remove spaces that terminate digit sequences.)
    \expandafter\passfirsttosecond\expandafter{\x}{%
      \name\passfirsttosecond{\x}{ \NewDocumentCommand}{}%<- the space before  \NewDocumentCommand
    }%                                                   %   must be as it terminates \romannumeral0's
  }%                                                     %   searching for more digits. Thus \romannumeral 
}%                                                       %   only finds the non-positive number 0, swallows
                                                         %   that number not returning any token for it.
\scratchy    

\message{%
  Show the toplevel-definitions done by \string\NewDocumentCommand:^^J^^J%
}

\show\one
\show\two

\message{%
  You can also have a nice name-show also exhibiting the^^J%
  internal commands defined by \string\NewDocumentCommand:^^J^^J%
}

\name\show{one}
\name\show{one }
\name\show{one code}

\name\show{two}
\name\show{two }
\name\show{two code}

\begin{document}

\one

\two

\end{document}

在此处输入图片描述

在此处输入图片描述


另一种变体是,通过以下方式对用户级宏和内部宏的分配进行\NewDocumentCommand“全球化” :\globel\let⟨macro⟩=⟨macro⟩

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand\passfirsttosecond[2]{#2{#1}}%
\newcommand\exchange[2]{#2#1}%
%%---------------------------------------------------------------
%%  \name <token sequence _without_ curly braces>{<tokens that yield the sequence "macroname">}  
%%  -> <token sequence _without_ curly braces>\macroname
%%
%%  Examples: \name{bar} -> \bar
%%            \name\newcommand*{bar}... -> \newcommand*\bar...
%%            \name\outer\global\long\def{bar}...  -> \outer\global\long\def\bar...
%%            \name\string{bar} -> \string\bar
%%            \name\name\let{foo}={bar} 
%%               -> \name\let\foo={bar}
%%               -> \let\foo=\bar
%%
%% The gist of the trick is: Due to #1#-notation \name processes one
%% argument which is delimited by a left-curly-brace.
%% Unlike other argument-delimiters the delimiting left-curly-brace will
%% not be removed but will be left in place when (La)TeX reads the
%% argument.
%% The tokens at the left of the delimiting left-curly-brace are to be
%% prepended after applying \csname...\endcsname to the tokens nested in
%% curly braces.
%%
\csname @ifdefinable\endcsname\name{%
  \long\def\name#1#{\romannumeral0\nameinternal{#1}}%
}%
\newcommand\nameinternal[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%

\foreach \x in {one, two}{%
  \expandafter\passfirsttosecond\expandafter{\x}{%
    \name\NewDocumentCommand{\x}{}%
  }%
  \name\name\global\let{\x}={\x}%
  \name\name\global\let{\x\space}={\x\space}%
  \name\name\global\let{\x\space code}={\x\space code}%
}%

\message{%
  Show the toplevel-definitions done by \string\NewDocumentCommand:^^J^^J%
}

\show\one
\show\two

\message{%
  You can also have a nice name-show also exhibiting the^^J%
  internal commands defined by \string\NewDocumentCommand:^^J^^J%
}

\name\show{one}
\name\show{one }
\name\show{one code}

\name\show{two}
\name\show{two }
\name\show{two code}

\begin{document}

\one

\two

\end{document}

输出(除了行号\show)与第一个例子相同。

细微的差别在于:

对于第一个示例,只有宏\scratchy会被全局更改。 \one并且\two所属的内部宏将仅在当前范围内定义。

在第二个示例中\one,及其\two所属的内部宏将被全局(重新)定义。

我写“(重新)定义”是因为像\newcommandLaTeX 2e 内核那样\NewDocumentCommand,如果宏在当前范围内未定义但在某个上级范围内定义,则不会抛出错误消息。\global\let...如果要定义的控制序列已经在当前范围内或某个上级范围内定义,则也不会抛出错误消息。

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foobar}{}{This is foobar}
\show\foobar
\expandafter\show\csname foobar \endcsname
\expandafter\show\csname foobar code\endcsname

% This will throw an error-message:
\NewDocumentCommand{\foobar}{}{This is another foobar}
\show\foobar
\expandafter\show\csname foobar \endcsname
\expandafter\show\csname foobar code\endcsname

\begingroup
\let\foobar=\UnDEFInED
% This will not throw an error-message... :
\NewDocumentCommand{\foobar}{}{This is yet another foobar}
\global\let\foobar=\foobar
\expandafter\global\expandafter\let\csname foobar \expandafter\endcsname\expandafter=\csname foobar \endcsname
\expandafter\global\expandafter\let\csname foobar code\expandafter\endcsname\expandafter=\csname foobar code\endcsname
\endgroup

% ... but \foobar is redefined outside the local scope also/is redefined in
% all scopes:

\show\foobar
\expandafter\show\csname foobar \endcsname
\expandafter\show\csname foobar code\endcsname

\begin{document}
\end{document}

相关内容