我有许多命令需要最多 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⟩
!!! 谨慎使用 !!!
\ReplacementScope
作为副作用,还通过匹配花括号 / !!!替换 catcode 1(开始组)/2(结束组)的所有匹配显式字符标记对{1(begin group)
}2(end group)
⟨tokens to replace⟩
不能包含 catcode 1(开始组)或 2(结束组)的明确字符标记!!!⟨tokens to replace⟩
不能包含 catcode 6(参数)的标记!!!- 定义临时宏,因此无法完全扩展!!!
- 您需要注意替换的正确顺序!!!
您可以使用它\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
,则可以使用X
like {
and,例如,do
\def\tempaXdefinition of tempa}%
:。
这将被标记为:
\def
,,\tempa
X1
,,,,,,,,,,,,,,,,,,,,,,,,,。d11
e11
f11
i11
n11
i11
t11
i11
o11
n11
⟨space⟩10
o11
f11
⟨space⟩10
t11
e11
m11
p11
a11
}2
如果在 内完成\ReplacementScope
,那么这将转向
\def
,,\tempa
{1
,,,,,,,,,,,,,,,,,,,,,,,,,。d11
e11
f11
i11
n11
i11
t11
i11
o11
n11
⟨space⟩10
o11
f11
⟨space⟩10
t11
e11
m11
p11
a11
}2
乍一看这并不重要。
假设由于某种不为人知的原因,您希望应用#{
-notation 来定义一个宏\tempa
,该宏的参数由类别代码 1(开始组)的显式 X 字符标记分隔,并将保留在原处。
您可以这样做:
\catcode`\X=1
\def\tempa#1#Xdefinition of tempa}%
第二行将被标记为:
\def
,,,,,,\tempa
#6
112
#6
X1
,,,,,,,,,,,,,,,,,,,,,,,,,。d11
e11
f11
i11
n11
i11
t11
i11
o11
n11
⟨space⟩10
o11
f11
⟨space⟩10
t11
e11
m11
p11
a11
}2
这里面\ReplacementScope
会变成:
\def
,,,,,,\tempa
#6
112
#6
{1
,,,,,,,,,,,,,,,,,,,,,,,,,。d11
e11
f11
i11
n11
i11
t11
i11
o11
n11
⟨space⟩10
o11
f11
⟨space⟩10
t11
e11
m11
p11
a11
}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/+v
xparse 的 -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}