这当然是重复的,但我找不到直接简单的转换方法。
是否有一种通用的方法来\command{x}{y}
转换\commandEnhanced{x,y}
?
(这个问题很普遍,这就是我不提供 MWE 的原因。)
编辑
因此现在,真正进入问题的核心(我承认主要是出于好奇),即如何编写转换命令,\enhance
比如,对于任何语法为的命令\command{x}{y}
,该命令\enhance{\command}
都会创建一个新命令\commandEnhanced{x,y}
?
我不知道嵌套参数,所以我使用“Arg?”来代替,但是那样会是这样的:
\newcommand{\enhance}[1]{%
\NewDocumentCommand{\#1EnhancedAUX}{mm}{--#Arg1?--#Arg2?--}
\NewDocumentCommand{\#1Enhanced}{>{\SplitArgument{1}{,}}m}{\EnhancedAUX#Arg?}}
我只是不知道除了“Arg?”以外还要输入什么。
答案1
让我尝试解释一下这个问题。
你知道如何定义一个双参数命令,比如
\newcommand{\command}[2]{--#1--#2--}
但希望用户语法是
\command{x,y}
而不是\command{x}{y}
上述定义所要求的。
以下是\NewDocumentCommand
救援方法:
\NewDocumentCommand{\command}{>{\SplitArgument{1}{,}}m}{\commandaux#1}
\NewDocumentCommand{\commandaux}{mm}{--#1--#2--}
“预处理器指令”\SplitArgument{1}{,}
告诉 LaTeX,在第一次出现时应该检查并拆分参数,
;然后这个逗号之前和之后的部分将被括在括号中。
因此,通过调用\command{x,y}
,LaTeX 接下来会看到
\commandaux{x}{y}
注意:如果缺少逗号,第二个括号组将包含特殊标记列表-NoValue-
,您可以对其进行测试
\NewDocumentCommand{\commandaux}{mm}{%
--#1--%
\IfNoValueF{#2}{#2--}%
}
这样,\command{x,y}
就会产生--x--y--
,而\command{x}
会产生--x--
。
只是为了好玩
如果\command
是使用 定义的“简单命令”,\newcommand
并且没有可选参数,则可以使用以下想法来访问参数的数量https://tex.stackexchange.com/a/627923/4427
因此我们可以减去 1 并将所需的数字传递给\SplitArgument
。这允许使用适当数量的括号组调用原始命令。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\enhance}{m}
{
\krebs_enhance:N #1
}
\cs_new_protected:Nn \krebs_enhance:N
{
\exp_args:Ncx
\NewDocumentCommand % jump over this
{\cs_to_str:N #1 Enhanced} % form a csname
{>{\SplitArgument{\int_eval:n { \str_count:e { \cs_argument_spec:N #1 } / 2 - 1}}{,}}m}
{ #1##1 }
}
\cs_generate_variant:Nn \str_count:n { e }
\ExplSyntaxOff
\newcommand{\command}[2]{--#1--#2--}
\enhance{\command}
\begin{document}
\command{x}{y}
\commandEnhanced{x,y}
\end{document}
如果您输入的项目少于要求的项目,您将看到-NoValue-
弹出窗口。
或者
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\enhance}{m}
{
\krebs_enhance:N #1
}
\cs_new_protected:Nn \krebs_enhance:N
{
\exp_args:Nc \NewDocumentCommand { \cs_to_str:N #1 Enhanced } { m }
{
\exp_last_unbraced:Ne #1 { \clist_map_function:nN { ##1 } \__krebs_enhance_brace:n }
}
}
\cs_new:Nn \__krebs_enhance_brace:n { {#1} }
\ExplSyntaxOff
\newcommand{\command}[2]{--#1--#2--}
\enhance{\command}
\begin{document}
\command{x}{y}
\commandEnhanced{x,y}
\end{document}
此处处理参数以支撑每个项目。如果您提供的项目少于要求,则需要自行处理。
答案2
如果您接受带有括号的另一种语法:\command(x,y)
,那么您可以非常简单地定义宏:
\def\command(#1,#2){first: #1, second #2.}
如果坚持使用大括号:\command{x,y}
,那么必须先删除这些大括号,然后才能使用之前的定义:
\def\command#1{\commandX(#1)}
\def\commandX(#1,#2){first: #1, second #2.}
test: \command{x,y}
\bye
答案3
答案4
您的请求包含两个任务:
- 将逗号列表映射到无分隔参数列表的例程,其中添加了宏标记以处理分隔参数。
\enhance{\command}
定义应用映射例程的例程\commandEnhanced
,从而将标记\command
添加到由映射产生的未限定参数列表的前面。
如果您希望在用户提供的逗号列表不包含所需的强制参数那么多项或某些逗号列表项指定为空/空白的情况下指定强制参数的默认值,我可以提供一个界面
\PassCommaListAsUndelimitedListTo {⟨comma list⟩}%
{⟨emptiness or (message-delivering) tokens to
prepend to ⟨tokens where undelimited arguments are to be
appended⟩ in case comma-list has more items
than defaults are provided⟩}%
{⟨tokens where undelimited arguments are to be appended⟩}%
{
{⟨default for undelimited argument 1⟩}
{⟨default for undelimited argument 2⟩}
...
{⟨default for undelimited argument k⟩}
}
因此,此接口的语法\enhance
为
\enhance{⟨macro⟩}
{%
⟨emptiness or (message-delivering) tokens to prepend to the call
of ⟨macro⟩ in case ⟨macro⟩Enhanced's
comma-list-argument has more items than there are defaults⟩
}%
{%
{⟨default for ⟨macro⟩'s 1st undelimited argument⟩}%
{⟨default for ⟨macro⟩'s 2nd undelimited argument⟩}%
...
{⟨default for ⟨macro⟩'s k-th undelimited argument⟩}%
}
如果⟨宏⟩用户提供的增强型逗号列表包含比默认值更多的元素,虚假的逗号列表项将被忽略,并且⟨空值或(消息传递)标记添加到前面...⟩被添加到调用的标记前面⟨宏⟩。
即要附加到的未限定参数的数量⟨需要附加未限定参数的标记⟩/⟨宏⟩在任何情况下都对应于指定的默认值数量。
\documentclass{article}
\ExplSyntaxOn
%% --------------------------------------------------------------------------------
%% Stuff for error-messages:
\msg_new:nnn { PassCommaListAsUndelimitedList }
{ too-many-comma-items }
{ #1Enhanced 's~argument~holds~a~comma-list~with~more~than~#2~components~
although~there~should~be~at~most~#2~components~because~the~underlying~
macro~#1~processes~#2~undelimited~arguments.~
Spurious~components~ignored.}
\prop_gput:Nnn \g_msg_module_type_prop { PassCommaListAsUndelimitedList } {}
\prop_gput:Nnx \g_msg_module_name_prop { PassCommaListAsUndelimitedList } {LaTeX}
\cs_new:Npn \PassCommaListAsUndelimitedListToTooManyItemsError #1#2 {%
% #1 macro token
% #2 word or digits denoting amount of arguments
\exp_args:Nno
\use:n
{ \msg_error:nnnn { PassCommaListAsUndelimitedList } { too-many-comma-items } }
{
\exp:w\exp_after:wN\exp_after:wN\exp_after:wN\exp_after:wN
\exp_after:wN\exp_after:wN\exp_after:wN\exp_end:
\exp_after:wN\exp_after:wN\exp_after:wN \c_backslash_str\cs_to_str:N#1
}
{ #2 }
}%
%
% \PassCommaListAsUndelimitedListToTooManyItemsError{\macro}{<amount>}
%
% yields an error-message
%
% \macroEnhanced's argument holds a comma list with more than <amount> components
% although there should be at most <amount> components because the underlying
% macro \macro processes <amount> undelimited arguments.
% Spurious components ignored.
%% --------------------------------------------------------------------------------
%% Map code to items of comma-list:
% \PassCommaListAsUndelimitedList_clist_map_tokens:nn {<comma list>}
% {<code if item is not blank>}
% {<code if item is blank>}
% Calls <code if item is not blank>{<item>} for every non-blank <item> stored in the <comma list>.
% Calls <code if item is blank>{<item>} for every blank <item> stored in the <comma list>.
\cs_new:Npn \PassCommaListAsUndelimitedList_clist_map_tokens:nnn #1#2#3
% #1 - comma list
% #2 - code if item is not blank
% #3 - code if item is blank
{
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {{#2}{#3}}
\prg_do_nothing: #1 , \s__clist_stop \clist_map_break: ,
\prg_break_point:Nn \clist_map_break: { }
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw #1#2 ,
{
\__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
\tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
\__clist_map_unbrace:wn , {\tl_if_empty:oTF{\use_none:nn #2 ? }{\use_ii:nn}{\use_i:nn}#1}
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {#1} \prg_do_nothing:
}
\cs_new:Npn \__clist_map_unbrace:wn #1, #2 { #2 {#1} }
%% --------------------------------------------------------------------------------
%% Map comma-list to list of undelimited arguments and prepend tokens for processing
%% undelimited arguments and - in case there are more comma-items than defaults -
%% prepend some error- or warning-message-tokens.
%
% \PassCommaListAsUndelimitedListTo{<comma-list>}
% {<message-delivering tokens to prepend to #3 in case comma-list
% has more items than defaults are provided>}
% {<tokens where undelimited arguments are to be appended>}
% {<list of undelimited arguments denoting defaults in case
% corresponding comma-list-item is specified blank or not
% enough comma-list-items are specified>}
%
% E.g., \PassCommaListAsUndelimitedListTo{A, B, C}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: \macro{A}{B}{C}
%
% E.g., \PassCommaListAsUndelimitedListTo{A, , C}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: \macro{A}{Default for \macro's 2nd arg}{C}
%
% E.g., \PassCommaListAsUndelimitedListTo{A}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: \macro{A}{Default for \macro's 2nd arg}{Default for \macro's 3rd arg}
%
% E.g., \PassCommaListAsUndelimitedListTo{A, B, C, D}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: Problem\macro{A}{B}{C}
%
% E.g., \PassCommaListAsUndelimitedListTo{A, B, , D}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: Problem\macro{A}{B}{Default for \macro's 3rd arg}
%
% E.g., \PassCommaListAsUndelimitedListTo{A, {}, C}{Problem}{\macro}{%
% {Default for \macro's 1st arg}
% {Default for \macro's 2nd arg}
% {Default for \macro's 3rd arg}
% }%
% yields: \macro{A}{}{C}
\cs_new:Npn \PassCommaListAsUndelimitedListTo #1#2#3#4 {
% #1 comma-list
% #2 tokens to prepend to #3 in case comma-list has more items than defaults are provided
% #3 tokens where undelimited arguments are to be appended
% #4 defaults for undelimited arguments
\exp_args:Nno \use:nn
{ \PassCommaListAsUndelimitedList_clist_map_tokens:nnn {#1}
{\__PassCommaListAsUndelimitedList_grab_next_nonblank:nnwnn{#2}}
{\__PassCommaListAsUndelimitedList_grab_next_blank:nnwnn{#2}}
\__PassCommaListAsUndelimitedList_Reserved:nn {#3}
} { \exp:w \tl_trim_spaces_apply:nN {#4} \exp_end: }
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_Reserved:nn #1#2 {
% #1 - code that shall prepend undelimited arguments and undelimited arguments gathered so far
% #2 - remaining defaults for undelimited argument
#1#2
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_grab_next_nonblank:nnwnn #1#2#3\__PassCommaListAsUndelimitedList_Reserved:nn #4#5 {
% #1 - code to prepend in case comma-list has more items than defaults are provided
% #2 - current comma-list-item
% #3 - tokens forming the next `\clist_map_tokens:nn`-iteration
% #4 - code that shall prepend undelimited arguments and undelimited arguments gathered so far
% #5 - defaults
\tl_if_empty:oTF { \use_none:nn ? #5 ? }
{\clist_map_break:n{#1#4} #3 }
{
\exp_args:Nno
\use:n
{
#3 \__PassCommaListAsUndelimitedList_Reserved:nn {#4{#2}}
} {\use_i:nn {} #5}
}
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_grab_next_blank:nnwnn #1#2#3\__PassCommaListAsUndelimitedList_Reserved:nn #4#5 {
% #1 - code to prepend in case comma-list has more items than defaults are provided
% #2 - current comma-list-item
% #3 - tokens forming the next `\clist_map_tokens:nn`-iteration
% #4 - code that shall prepend undelimited arguments and undelimited arguments gathered so far
% #5 - defaults
\tl_if_empty:oTF { \use_none:nn ? #5 ? }
{\clist_map_break:n{#1#4} #3 }
{
\exp_args:Nno
\use:n
{
\exp_args:Nno \use:n
{ #3 \__PassCommaListAsUndelimitedList_Reserved:nn }
{ \exp:w \exp_args:Nno \use:n { \exp_end: #4} {\tl_head:w #5 {} \q_stop} }
} {\use_i:nn {} #5}
}
}
%% --------------------------------------------------------------------------------
%% Define \<macro>Enhanced to process a comma-list by mapping it to a list of undelimited
%% arguments where the token <macro> is prepended and where - in case the comma-list has
%% more items than defaults for macro are provided - some tokens for delivering message are
%% prepended to the token \macro
%%
%% Syntax:
%%
%% \enhance{<macro>}%
%% {<additional error-tokens in case <macro>Enhanced's comma-list-argument
%% has more items than there are defaults>}%
%% {%
%% {<default for <macro>'s 1st argument in case it is not provided via <macro>Enhanced's comma-list>}%
%% {<default for <macro>'s 2nd argument in case it is not provided via <macro>Enhanced's comma-list>}%
%% ...
%% {<default for <macro>'s k-th argument in case it is not provided via <macro>Enhanced's comma-list>}%
%% }%
%%
\cs_new:Npn \enhance #1 #2 #3{
% #1 macro
% #2 tokens to prepend to macro in case comma-list has more items than defaults for macro are provided
% #3 defaults
\cs_new:cpn {\cs_to_str:N #1 Enhanced} ##1 {
\PassCommaListAsUndelimitedListTo { ##1 }{ #2 }{ #1 }{ #3 }
}
}
\ExplSyntaxOff
\newcommand\macro[3]{%
{\frenchspacing
\textnormal{Arg 1: (#1)}\\
\textnormal{Arg 2: (#2)}\\
\textnormal{Arg 3: (#3)}
}%
}%
\enhance{\macro}%
{\PassCommaListAsUndelimitedListToTooManyItemsError{\macro}{three}}%
{
{Default for \texttt{\string\macro}'s argument 1}
{Default for \texttt{\string\macro}'s argument 2}
{Default for \texttt{\string\macro}'s argument 3}
}
\begin{document}
\enlargethispage{4cm}\null\par\kern-3cm
\noindent \verb|\macroEnhanced{A, B, C}|:\\
\macroEnhanced{A, B, C}
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, , C}|:\\
\macroEnhanced{A, , C}
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, , }|:\\
\macroEnhanced{A, , }
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, {} , {}}|:\\
\macroEnhanced{A, {} , {}}
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A}|:\\
\macroEnhanced{A}
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, B}|:\\
\macroEnhanced{A, B}
\bigskip\hrule\bigskip
%% The following yields an error-message about \macroEnhanced's argument
%% holding a comma-list with more than three components although there
%% should be at most three components.
%% The raising of this error-message is intended/correct behavior.
%% As \macro processes only three undelimited arguments, three defaults
%% for these arguments were specified when calling \enhance for defining
%% \macroEnhanced.
%% If the comma-list that forms \macroEnhanced's argument provides more
%% than three arguments/provides more arguments than defaults were provided,
%% this is a problem where the user should be informed about by means of a
%% message.
%% The following is a test whether the message intended/needed in this case
%% is really raised.
%%
\noindent \verb|\macroEnhanced{A, B, C, D, E}|:\\
\macroEnhanced{A, B, C, D, E}
{\itshape
\bigskip
\noindent On the console and in the log-file you also get something like:
\begin{verbatim}
! LaTeX Error: \macroEnhanced's argument holds a comma-list with more than
(LaTeX) three components although there should be at most three
(LaTeX) components because the underlying macro \macro processes
(LaTeX) three undelimited arguments. Spurious components ignored.
Type <return> to continue.
...
l.305 \macroEnhanced{A, B, C, D, E}
\end{verbatim}
\noindent This is the intended behavior.
}
\end{document}