命名 \newcommand 参数以提高代码清晰度

命名 \newcommand 参数以提高代码清晰度

我有许多命令需要最多 8 个参数,其中有许多地方使用各种参数。如果能够在命令中命名参数以使代码更易于理解,那就太好了。这是一个使用 C 预处理器的 MWE,显示了我想要实现的目标。如何在没有 cpp 的情况下做到这一点?

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}

\newcommand{\mydraw}[1]{%

#define MYVALUE #1

  \addplot [mark=*] coordinates{(MYVALUE,0.3)};
  \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
}
 
\begin{document}

\begin{tikzpicture}
  \begin{axis}

    \addplot coordinates{(0,0) (1,1)};

    \mydraw{0.4}
    \mydraw{0.6}

    \end{axis}
\end{tikzpicture}

\end{document}

所需的输出如下所示:

在此处输入图片描述

下面显示了我尝试过的一些方法。目前,我使用 pgfkeys 和 pgfplotsextra,如 (tikz 中 addplot 和 draw 的扩展有什么不同吗?),但这正变得越来越成问题,因为,例如,多次调用 \mydraw 等命令会失败。我正在寻找一个比 pgfkeys 更简单的解决方案。对我来说,这很困难的部分原因是 draw 中发生的奇怪的两遍事件。其中一些可以编译,但由于两遍处理,其中一个点的标签被删除。

% This I can't get to compile to test.
%\let\MYVALUE=#1

% \def doesn't have the command/renewcommand problem.  It
% just fails the two pass thing.
%
%\def\MYVALUE{#1}

% \newcommand doesn't work because I have to know when I
% am redefining something to use \renewcommand.  If I define
%\newcommand \MYVALUE outside of \mydraw, it fails the two
% pass thing.
%
%\newcommand\MYVALUE{#1}

%  \addplot [mark=*] coordinates{(\MYVALUE,0.3)};
%  \draw node at (axis cs:\MYVALUE,0.4) [below] {\MYVALUE};

请注意,我并没有试图这样做(如何命名命令参数)。从调用者的角度来看,这应该看起来像一个“正常”命令。我试图在命令中为调用者不可见的参数命名。

更新:

在提供的答案中,egreg 的答案是按照问题的原始定义给出的最佳答案。我已将其标记为答案。

我并不期望答案会重写命令的声明,因此我没有在问题中包含可选参数的需要。考虑到这种需要,名称定义评论中提供的答案是我正在部署的实际答案。

答案1

只要占位符没有以不同的方式使用并且仅由 ASCII 字符组成,您就可以这样做

\documentclass{article}
%\usepackage{xparse} % not needed with LaTeX 2020-10-01 or later

\ExplSyntaxOn
\NewDocumentCommand{\varnewcommand}{mmm}
 {
  % #1 = command to define
  % #2 = list of arguments
  % #3 = replacement text
  \donham_varnewcommand:Nnn #1 { #2 } { #3 }
 }

\seq_new:N \l__donham_varnewcommand_args_seq
\tl_new:N \l__donham_varnewcommand_replacement_tl
\tl_new:N \l__donham_varnewcommand_name_tl
\cs_generate_variant:Nn \cs_set:Nn { cV }

\cs_new_protected:Nn \donham_varnewcommand:Nnn
 {
  % make a sequence from the argument name list
  \seq_set_from_clist:Nn \l__donham_varnewcommand_args_seq { #2 }
  % build an internal function name from the command we want to define
  % it needs to end in : followed by as many n as the number of args
  \tl_set:Nx \l__donham_varnewcommand_name_tl
   {
    \cs_to_str:N #1 : \prg_replicate:nn { \seq_count:N \l__donham_varnewcommand_args_seq } { n }
   }
  % save the replacement text in a tl variable
  \tl_set:Nn \l__donham_varnewcommand_replacement_tl { #3 }
  % now cycle through the sequence of arguments
  \seq_map_indexed_function:NN \l__donham_varnewcommand_args_seq \__donham_varnewcommand_replace:nn
  % the replacements have been done; define the internal function
  \cs_set:cV  { \l__donham_varnewcommand_name_tl } \l__donham_varnewcommand_replacement_tl
  % now set #1 to be the same as the just built function
  \cs_new_eq:Nc #1 { \l__donham_varnewcommand_name_tl }
 }

\cs_new_protected:Nn \__donham_varnewcommand_replace:nn
 {
  % replace the k-th item in the sequence with #<k>
  \regex_replace_all:nnN { #2 } { \cP\# #1 } \l__donham_varnewcommand_replacement_tl
 }

\ExplSyntaxOn

\varnewcommand{\mydraw}{MYVALUE}{%
  \addplot [mark=*] coordinates{(MYVALUE,0.3)};
  \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
}

\varnewcommand{\foo}{AAA,BBB,CCC}{%
  AAA-BBB-CCC-AAA%
}

的第二个参数\varnewcommand应为字符串列表,顺序不限。第一个项目将被替换为#1,第二个项目将被替换为#2,依此类推。

这将转化为

> \mydraw=\long macro:
#1->\addplot [mark=*]coordinates{(#1,0.3)};\draw nodeat(axiscs:#1,0.4)[below]{#1};.
l.47 \show\mydraw

?
> \foo=\long macro:
#1#2#3->#1-#2-#3-#1.
l.49 \show\foo

答案2

您使用pgfplots基于 pgf 的,因此您可以使用 pgf 键。

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\pgfkeys{/my args/.cd,my value/.initial=0}%

\newcommand{\mydraw}[1]{%
\pgfkeys{/my args/.cd,#1}%
\edef\temp{% <- this is necessary because of the way pgfplots surveys and executes stuff
  \noexpand\addplot [mark=*] coordinates{(\pgfkeysvalueof{/my args/my value},0.3)};
  \noexpand\draw node at (axis cs:\pgfkeysvalueof{/my args/my value},0.4) [below] {\pgfkeysvalueof{/my args/my value}};
  }%
\temp  
}
 
\begin{document}

\begin{tikzpicture}
  \begin{axis}

    \addplot coordinates{(0,0) (1,1)};

    \mydraw{my value=0.4}
    \mydraw{my value=0.6}

    \end{axis}
\end{tikzpicture}

\end{document}

在此处输入图片描述

当然,你可以定义一个你不需要的版本my value=,但是这样更容易造成混淆。

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\pgfkeys{/my args/.cd,my value/.initial=0}%

\newcommand{\mydraw}[1]{%
\pgfkeys{/my args/.cd,my value=#1}%
\edef\temp{% <- this is necessary because of the way pgfplots surveys and executes stuff
  \noexpand\addplot [mark=*] coordinates{(\pgfkeysvalueof{/my args/my value},0.3)};
  \noexpand\draw node at (axis cs:\pgfkeysvalueof{/my args/my value},0.4) [below] {\pgfkeysvalueof{/my args/my value}};
  }%
\temp  
}
 
\begin{document}

\begin{tikzpicture}
  \begin{axis}

    \addplot coordinates{(0,0) (1,1)};

    \mydraw{0.4}
    \mydraw{0.6}

    \end{axis}
\end{tikzpicture}

\end{document}

答案3

我只能即兴地提供一个\ReplacementScope带有两个参数的宏:

第一个参数是用逗号分隔的 -pair 列表。⟨tokens to replace⟩=⟨replacement⟩

第二个参数是⟨brace balanced list of tokens⟩

⟨brace balanced list of tokens⟩每个实例中,⟨tokens to replace⟩将被替换⟨replacement⟩为每个对。⟨tokens to replace⟩=⟨replacement⟩

!!! 谨慎使用 !!!

  1. \ReplacementScope作为副作用,还通过匹配花括号 /  !!!替换 catcode 1(开始组)/2(结束组)的所有匹配显式字符标记对{1(begin group)}2(end group)
  2. ⟨tokens to replace⟩不能包含 catcode 1(开始组)或 2(结束组)的明确字符标记!!!
  3. ⟨tokens to replace⟩不能包含 catcode 6(参数)的标记!!!
  4. 定义临时宏,因此无法完全扩展!!!
  5. 您需要注意替换的正确顺序!!!

您可以使用它\ReplacementScope来替换文本字符串,例如 by#1或 by#2##1任何其他的。

广告项目 1:

在正常的 catcode-régime 下,catcode 1 的唯一字符是左花括号,catcode 2 的唯一字符是右花括号。因此,在正常的 catcode-régime 下,通过匹配花括号替换 catcode 1/2 的字符标记不会产生差异。即使在异常的 catcode-régime 下对事物进行了标记,替换通常也无关紧要。但存在极端情况。

例如,如果您已为角色分配了 catcode 1 X,则可以使用Xlike {and,例如,do
\def\tempaXdefinition of tempa}%:。

这将被标记为:

\def,,\tempaX1,,,,,,,,,,,,,,,,,,,,,,,,,。​​​​​​​​​​​​​​​​​​​d11e11f11i11n11i11t11i11o11n11⟨space⟩10o11f11⟨space⟩10t11e11m11p11a11}2

如果在 内完成\ReplacementScope,那么这将转向

\def,,\tempa{1,,,,,,,,,,,,,,,,,,,,,,,,,。​​​​​​​​​​​​​​​​​​​d11e11f11i11n11i11t11i11o11n11⟨space⟩10o11f11⟨space⟩10t11e11m11p11a11}2

乍一看这并不重要。

假设由于某种不为人知的原因,您希望应用#{-notation 来定义一个宏\tempa,该宏的参数由类别代码 1(开始组)的显式 X 字符标记分隔,并将保留在原处。
您可以这样做:

\catcode`\X=1
\def\tempa#1#Xdefinition of tempa}%

第二行将被标记为:

\def,,,,,,\tempa​​​​#6112#6X1,,,,,,,,,,,,,,,,,,,,,,,,,。​​​​​​​​​​​​​​​​​​​d11e11f11i11n11i11t11i11o11n11⟨space⟩10o11f11⟨space⟩10t11e11m11p11a11}2

这里面\ReplacementScope会变成:

\def,,,,,,\tempa​​​​#6112#6{1,,,,,,,,,,,,,,,,,,,,,,,,,。​​​​​​​​​​​​​​​​​​​d11e11f11i11n11i11t11i11o11n11⟨space⟩10o11f11⟨space⟩10t11e11m11p11a11}2

这有所不同:

而不是由的参数来分隔,而将由 来分隔,这会影响分隔符匹配。X1(begin group)\tempa{1(begin group)

广告项目 5:

替换发生的顺序有所不同:

例如,

\ReplacementScope{VARIABLEA=#1, INNERVARIABLEA=##1}{%
  \newcommand\Outside[1]{%
    \newcommand\Inside[1]{%
      VARIABLEA and INNERVARIABLEA
    }%
  }%
}%

产量

\newcommand\Outside[1]{%
  \newcommand\Inside[1]{%
    #1 and INNER#1
  }%
}%

然而

\ReplacementScope{INNERVARIABLEA=##1, VARIABLEA=#1}{%
  \newcommand\Outside[1]{%
    \newcommand\Inside[1]{%
      VARIABLEA and INNERVARIABLEA
    }%
  }%
}%

产量

\newcommand\Outside[1]{%
  \newcommand\Inside[1]{%
    #1 and ##1
  }%
}%

我认为,只要您不进行令人费解的纯 TeX 欺骗,您就会非常安全。\def\macro#1#⟨non-brace-character of catcode 1⟩

尽管如此,我并不是很喜欢这样的事情:

首先,您会得到一个更高的处理级别,在跟踪错误时您必须考虑到这一点。

其次,我看到有人试图将与 TeX 中提供的编程范式以外的内容转移到 TeX。根据我的经验,这样的工作很容易遇到困难,只有非常熟悉 TeX 的编程范式才能解决。但如果你非常熟悉 TeX 的编程范式,那么你就不需要从其他编程领域进行这样的转移。

第三,替换任务可能由编辑器/用于编写.tex 文件的软件来执行。

第四,基于LuaTeX的TeX引擎可能更适合这类工作。


\documentclass[a4paper]{article}

\makeatletter
%%//////////////////////// Code of \ReplacementScope: /////////////////////////
%%=============================================================================
\RequirePackage{pgfkeys}%
\pgfkeys{%
  /ReplacementScope/.unknown/.code=%
  \expandafter\UD@AddToReplacementlist\expandafter{\pgfkeyscurrentname}{#1},%
}%
\newcommand*\UD@Replacementlist{}%
\newcommand\UD@AddToReplacementlist[2]{%
  \expandafter\UD@Exchange
  \expandafter{\expandafter\toks@\expandafter{\the\toks@}}{%
    \toks@{\UD@Revert{#1}{#2}}%
    \ifx\UD@Replacementlist\empty
      \toks@\expandafter{\the\toks@{\endgroup\@firstofone}}%
    \else
      \toks@\expandafter{%
        \the\expandafter\toks@\expandafter{\UD@Replacementlist}%
      }%
    \fi
    \edef\UD@Replacementlist{\the\toks@}%
  }%
}%
\newcommand\UD@Revert[4]{%
  #3{\UD@Replace{#1}{#2}{#4}}%
}%
\newcommand\ReplacementScope[1]{%
  \begingroup
  \pgfkeys{/ReplacementScope/.cd,#1,}%
  \ifx\UD@Replacementlist\empty\endgroup\expandafter\@secondoftwo\fi
  \UD@Replacementlist{\@firstofone}%
}%
%%=============================================================================
%%//////////////////// End of code of \ReplacementScope: //////////////////////

%%
%%//////////////////// Code of my own replacement-routine: ////////////////////
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingTokens, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% 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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\z@\UD@secondoftwo}%
  {\expandafter\z@\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\z@\UD@firstoftwo}%
  {\expandafter\z@\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific 
%% token-sequence that does neither contain explicit character tokens of 
%% category code 1 or 2 nor contain tokens of category code 6:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%%                              {<a <token sequence> without explicit 
%%                                character tokens of category code
%%                                1 or 2 and without tokens of
%%                                category code 6>}%
%%                              {<internal token-check-macro>}%
%%                              {<tokens to be delivered in case
%%                                <argument which is to be checked> has
%%                                <token sequence> as leading tokens>}%
%%                              {<tokens to be delivered in case 
%%                                <argument which is to be checked>
%%                                does not have <token sequence> as
%%                                leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[3]{%
  \romannumeral\UD@CheckWhetherNull{#1}{\expandafter\z@\UD@secondoftwo}{%
    \expandafter\UD@secondoftwo\string{\expandafter
    \UD@@CheckWhetherLeadingTokens#3{\relax}#1#2}{}}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter\z@\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%%                              {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything 
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
  \@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
\UD@internaltokencheckdefiner{\UD@InternalExplicitSpaceCheckMacro}{ }%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \romannumeral\UD@ExtractFirstArgLoop{ABCDE\UD@SelDOm} yields  {A}
%%
%%   \romannumeral\UD@ExtractFirstArgLoop{{AB}CDE\UD@SelDOm} yields  {AB}
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
  \long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\z@#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% \UD@Replace{<token-sequence to replace>}%
%%            {<replacement>}%
%%            {<tokens afterwards>}
%%            {<token list>}
%%
%% Replaces all instances of <token-sequence to replace> in <token list>
%% by <replacement>. The result will be nested in curly braces and
%% preceeded by <tokens afterwards>.
%%
%% !!! Does also replace all pairs of matching explicit character tokens of
%%     catcode 1/2 by matching braces!!!
%% !!! <token-sequence to replace> must not contain explicit character tokens
%%     of catcode 1 or 2 !!!
%% !!! <token-sequence to replace> must not contain tokens of catcode 6 !!!
%% !!! Defines temporary macro \UD@temp, therefore not expandable !!!
%%-----------------------------------------------------------------------------
\newcommand\UD@Replace[4]{%
  % #1 - <token-sequence to replace>
  % #2 - <replacement>
  % #3 - <tokens afterwards>
  % #4 - <token list>
  \begingroup
  \UD@internaltokencheckdefiner{\UD@temp}{#1}%
  \expandafter\endgroup
  \romannumeral\UD@ReplaceLoop{#4}{}{#1}{#2}{#3}%
}%
\newcommand\UD@ReplaceLoop[5]{%
  % Do:
  %  \UD@internaltokencheckdefiner{\UD@temp}{<token-sequence to replace>}%
  %  \romannumeral\UD@ReplaceLoop{<token list>}%
  %                              {<sequence created so far, initially empty>}%
  %                              {<token-sequence to replace>}%
  %                              {<replacement>}%
  %                              {<tokens afterwards>}%
  %
  % #1 - <token list>
  % #2 - <sequence created so far, initially empty>
  % #3 - <token-sequence to replace>
  % #4 - <replacement>
  % #5 - <tokens afterwards>
  \UD@CheckWhetherNull{#1}{\z@#5{#2}}{%
    \UD@CheckWhetherLeadingTokens{#1}{#3}{\UD@temp}{%
      \expandafter\expandafter\expandafter\UD@ReplaceLoop
      \expandafter\expandafter\expandafter{%
        \expandafter\UD@firstoftwo\expandafter{\expandafter}\UD@temp#1%
      }{#2#4}%
    }{%
      \UD@CheckWhetherLeadingTokens{#1}{ }{\UD@InternalExplicitSpaceCheckMacro}{%
         \expandafter\UD@ReplaceLoop
         \expandafter{\UD@removespace#1}{#2 }%
      }{%
        \UD@CheckWhetherBrace{#1}{%
          \expandafter\expandafter\expandafter\UD@PassFirstToSecond
          \expandafter\expandafter\expandafter{%
          \expandafter\UD@PassFirstToSecond\expandafter{%
            \romannumeral\expandafter\expandafter\expandafter\z@
            \romannumeral\expandafter\UD@ReplaceLoop
              \romannumeral
              \UD@ExtractFirstArgLoop{#1\UD@SelDOm}{}{#3}{#4}{\@firstofone}%
          }{#2}}%
          {\expandafter\UD@ReplaceLoop\expandafter{\UD@firstoftwo{}#1}}%
        }{%
          \expandafter\UD@PassFirstToSecond\expandafter{%
            \romannumeral
            \expandafter\UD@Exchange
            \romannumeral\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{\z@#2}%
          }{\expandafter\UD@ReplaceLoop\expandafter{\UD@firstoftwo{}#1}}%
        }%
      }%
    }%
    {#3}{#4}{#5}%
  }%
}%
\makeatother
%%=============================================================================
%%///////////////// End of code of my own replacement-routine. ////////////////
\makeatother


\ReplacementScope{VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfirstcommand}[3]{%
    Argument 1 is VAR_A.\\
    Argument 2 is VAR_B.\\
    Argument 3 is VAR_C.
  }%
  %
}


\ReplacementScope{}{%
  %
  \newcommand{\mysecondcommand}[3]{%
    Three arguments are gobbled.
    This sentence contains the string VAR_A.\\
    This sentence contains the string VAR_B.\\
    This sentence contains the string VAR_C.\\
  }%
  %
}

\ReplacementScope{INNERVAR_A=##1, INNERVAR_B=##2, INNERVAR_C=##3, VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\mythirdcommand}[3]{%
    \newcommand{\myinnerthirdcommand}[3]{%
      Argument 1 is INNERVAR_A.\\
      Argument 2 is INNERVAR_B.\\
      Argument 3 is INNERVAR_C.
    }%
    \myinnerthirdcommand{VAR_A}{VAR_B}{VAR_C}%
  }%
  %
}


\usepackage{tikz}
\usepackage{pgfplots}

\ReplacementScope{MYVALUE=#1}{%
  %
  \newcommand{\mydraw}[1]{%
    \addplot [mark=*] coordinates{(MYVALUE,0.3)};
    \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
  }%
  %
}%


\begin{document}

\begingroup

\ttfamily\footnotesize

\begin{verbatim}
\ReplacementScope{VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfirstcommand}[3]{%
    Argument 1 is VAR_A.\\
    Argument 2 is VAR_B.\\
    Argument 3 is VAR_C.
  }%
  %
}
\end{verbatim}
\string\myfirstcommand=\meaning\myfirstcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{}{%
  %
  \newcommand{\mysecondcommand}[3]{%
    Three arguments are gobbled.
    This sentence contains the string VAR_A.\\
    This sentence contains the string VAR_B.\\
    This sentence contains the string VAR_C.\\
  }%
  %
}
\end{verbatim}
\string\mysecondcommand=\meaning\mysecondcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{INNERVAR_A=##1, INNERVAR_B=##2, INNERVAR_C=##3, VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\mythirdcommand}[3]{%
    \newcommand{\myinnerthirdcommand}[3]{%
      Argument 1 is INNERVAR_A.\\
      Argument 2 is INNERVAR_B.\\
      Argument 3 is INNERVAR_C.
    }%
    \myinnerthirdcommand{VAR_A}{VAR_B}{VAR_C}%
  }%
  %
}
\end{verbatim}
\string\mythirdcommand=\meaning\mythirdcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{MYVALUE=#1}{%
  %
  \newcommand{\mydraw}[1]{%
    \addplot [mark=*] coordinates{(MYVALUE,0.3)};
    \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
  }%
  %
}%
\end{verbatim}
\string\mydraw=\meaning\mydraw

\vfill\hrule\vfill\vfill

\endgroup

\newpage

\begin{tikzpicture}
  \begin{axis}

    \addplot coordinates{(0,0) (1,1)};

    \mydraw{0.4}
    \mydraw{0.6}

    \end{axis}
\end{tikzpicture}

\end{document}

在此处输入图片描述

在此处输入图片描述



2020年11月17日编辑:

我刚刚想到,可以用另一种方法摆脱第 1 至 4 项中提到的限制:

\ReplacementScope在 verbatim-catcode-régime 下读取其参数,例如v/+vxparse 的 -type,将其放入⟨brace balanced list of tokens⟩expl3-token-list-variable 中,并通过递归调用 expl3 的-function 迭代以逗号分隔的-pairs列表,然后将结果传递给重新标记和重新消化 - 此代码比我的其他方法中的代码短得多,并且您不需要:⟨tokens to replace⟩=⟨replacement⟩\keyval_parse:NNn\tl_replace_all:Nnn ⟨tl var⟩ {⟨old tokens⟩} {⟨new tokens⟩}\tex_scantokens:D\makeatletter..\makeatother

缺点:

由于替换指令是以逗号分隔的列表⟨tokens to replace⟩=⟨replacement⟩

  • \ReplacementScope不能用于替换包含至少一个逗号的短语。
  • \ReplacementScope不能用于替换带有前导和/或尾随空格的短语。

作为\ReplacementScope在 verbatim-catcode-régime 下读取和标记的进程参数,适用与命令相同的限制\verb

\documentclass[a4paper]{article}

%%//////////////////////// Code of \ReplacementScope: /////////////////////////
%%=============================================================================
\RequirePackage{xparse}%
\NewDocumentCommand\ReplacementScope{}{%
  \begingroup
  % Catcode of horizontal tab is not switched to 12(other) by xparse's
  % routine for reading v/+v-type-arguments, so let's do that now:
  \catcode`\^^I=12\relax
  \InnerReplacementScope
}%
\ExplSyntaxOn
\msg_new:nnnn{ReplacementScope}%
             {NoValueError}%
             {Line\ #2\ specifies\ to\ replace\ the\ phrase:\\#1\\But\ a\ replacement\ text\ is\ not\ specified.} 
             {If\ you\ want\ to\ replace\ things,\ then\ you\ need\ to\ specify\ both\ the\ phrase\ to\ replace\ and\ the\ replacement\ text.}
\cs_new:Nn \ReplacementScope_NovalueError:n {
  \group_begin:
  \cs_set:Nn \msg_error_text:n {##1~Error:}
  \msg_error:nnxx {ReplacementScope}{NoValueError}{#1}{\msg_line_number:}
  \group_end:
}
\cs_new:Nn \ReplacementScope_Replace:nn {
  \tl_replace_all:Nnn \l_tmpa_tl {#1}{#2}
}
\group_begin:
\char_set_catcode_other:N \^^M
\use:n{
  \group_end:
  \NewDocumentCommand\InnerReplacementScope{+v+v}{
    \group_end:\group_begin:
    \tl_set:Nn \l_tmpa_tl {#2}
    \keyval_parse:NNn \ReplacementScope_NovalueError:n \ReplacementScope_Replace:nn { #1 }
    \exp_args:Nnc \use:n { \exp_args:Nno \tl_put_right:Nn { \l_tmpa_tl } } {@percentchar}
    \exp_args:Nno \tl_put_left:Nn {\l_tmpa_tl} {\token_to_str:N \endgroup ^^M}
    % \tl_show:N \l_tmpa_tl
    \tex_newlinechar:D=\tex_endlinechar:D
    \exp_args:NV \tex_scantokens:D {\l_tmpa_tl}
  }
}
\ExplSyntaxOff
%%=============================================================================
%%//////////////////// End of code of \ReplacementScope: //////////////////////

\ReplacementScope{VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfirstcommand}[3]{%
    Argument 1 is VAR_A.\\
    Argument 2 is VAR_B.\\
    Argument 3 is VAR_C.
  }%
  %
}


\ReplacementScope|}VAR_A}=#1, }VAR_B}=#2, }VAR_C}=#3||%
  %
  \newcommand{\mysecondcommand}[3]{%
    Argument 1 is }VAR_A}.\\
    Argument 2 is }VAR_B}.\\
    Argument 3 is }VAR_C}.
  }%
  %
|

\ReplacementScope{}{%
  %
  \newcommand{\mythirdcommand}[3]{%
    Three arguments are gobbled.
    This sentence contains the string VAR_A.\\
    This sentence contains the string VAR_B.\\
    This sentence contains the string VAR_C.\\
  }%
  %
}

\ReplacementScope{INNERVAR_A=##1, INNERVAR_B=##2, INNERVAR_C=##3, VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfourthcommand}[3]{%
    \newcommand{\myinnerfourthcommand}[3]{%
      Argument 1 is INNERVAR_A.\\
      Argument 2 is INNERVAR_B.\\
      Argument 3 is INNERVAR_C.
    }%
    \myinnerfourthcommand{VAR_A}{VAR_B}{VAR_C}%
  }%
  %
}

\usepackage{tikz}
\usepackage{pgfplots}

\ReplacementScope{MYVALUE=#1}{%
  %
  \newcommand{\mydraw}[1]{%
    \addplot [mark=*] coordinates{(MYVALUE,0.3)};
    \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
  }%
  %
}%


\begin{document}

\begingroup

\advance\oddsidemargin -.75in\relax
\advance\textwidth 1.5in\relax
\advance\linewidth 1.5in\relax
\advance\hsize 1.5in\relax
\null\kern-1.7in \enlargethispage{.3in}%
\sloppy
\ttfamily\footnotesize\frenchspacing
\parindent=0ex

\begin{verbatim}
\ReplacementScope{VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfirstcommand}[3]{%
    Argument 1 is VAR_A.\\
    Argument 2 is VAR_B.\\
    Argument 3 is VAR_C.
  }%
  %
}
\end{verbatim}

\string\myfirstcommand=\meaning\myfirstcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope|}VAR_A}=#1, }VAR_B}=#2, }VAR_C}=#3||%
  %
  \newcommand{\mysecondcommand}[3]{%
    Argument 1 is }VAR_A}.\\
    Argument 2 is }VAR_B}.\\
    Argument 3 is }VAR_C}.
  }%
  %
|
\end{verbatim}

\string\mysecondcommand=\meaning\mysecondcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{}{%
  %
  \newcommand{\mythirdcommand}[3]{%
    Three arguments are gobbled.
    This sentence contains the string VAR_A.\\
    This sentence contains the string VAR_B.\\
    This sentence contains the string VAR_C.\\
  }%
  %
}
\end{verbatim}

\string\mythirdcommand=\meaning\mythirdcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{INNERVAR_A=##1, INNERVAR_B=##2, INNERVAR_C=##3, VAR_A=#1, VAR_B=#2, VAR_C=#3}{%
  %
  \newcommand{\myfourthcommand}[3]{%
    \newcommand{\myinnerfourthcommand}[3]{%
      Argument 1 is INNERVAR_A.\\
      Argument 2 is INNERVAR_B.\\
      Argument 3 is INNERVAR_C.
    }%
    \myinnerfourthcommand{VAR_A}{VAR_B}{VAR_C}%
  }%
  %
}
\end{verbatim}

\string\myfourthcommand=\meaning\myfourthcommand

\vfill\hrule\vfill

\begin{verbatim}
\ReplacementScope{MYVALUE=#1}{%
  %
  \newcommand{\mydraw}[1]{%
    \addplot [mark=*] coordinates{(MYVALUE,0.3)};
    \draw node at (axis cs:MYVALUE,0.4) [below] {MYVALUE};
  }%
  %
}%
\end{verbatim}

\string\mydraw=\meaning\mydraw

\vfill\hrule\vfill

\newpage

\endgroup

\begin{tikzpicture}
  \begin{axis}

    \addplot coordinates{(0,0) (1,1)};

    \mydraw{0.4}
    \mydraw{0.6}

    \end{axis}
\end{tikzpicture}

\end{document}

在此处输入图片描述 在此处输入图片描述

相关内容