我如何定义一个\myfun
函数
\myfun{a} -> a
\myfun{a \\ b} -> a
\myfun{a \\ b \\ c} -> a
\myfun{$\theta$ \\ b \\ c} -> $\theta$
答案1
您可以使用 来完成expl3
。
无法完全扩展:
\ExplSyntaxOn
\NewDocumentCommand{\myfun}{m}
{
\seq_set_split:Nnn \l_tmpa_seq { \\ } { #1 }
\seq_item:Nn \l_tmpa_seq { 1 }
}
\ExplSyntaxOff
对于完全可扩展的宏:
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\myfun}{m}
{
\test_myfun:w #1 \\ \q_stop
}
\cs_new:Npn \test_myfun:w #1 \\ #2 \q_stop
{
\tl_trim_spaces:n { #1 }
}
\ExplSyntaxOff
\begin{document}
.\myfun{a}. should be .a.
.\myfun{a \\ b}. should be .a.
\myfun{a \\ b \\ c} should be a
\myfun{$\theta$ \\ b \\ c} should be $\theta$
\edef\test{.\myfun{\textbf{a} \\ b \\ c}.}
\texttt{\meaning\test}
\end{document}
句号用于表示空格被修剪。同时还\textbf
表示\edef
这没有问题(因为\tl_trim_spaces:n
表现良好……)。
这个想法是将参数传递给\test_myfun:w
具有两个分隔参数;第一个结束于\\
,第二个结束于\q_stop
。这安全吗?相当安全,因为\q_stop
是“夸克”,所以它被定义为扩展为自身,因此在其他情况下无法使用。因此,它不太可能在 的参数中找到出路\myfun
。
:w
名称中的部分表示该函数具有“奇怪的”参数。
因此,在 和 的情况下a
,a \\ b
调用a \\ b \\ c
将是
\test_myfun:w a\\\qstop
\test_myfun:w a \\ b \\ q_stop
\test_myfun:w a \\ b \\ c \\ q_stop
(这里的空格很重要,但它们不在函数的定义中)。因此,在第一种情况下和其他两种情况下#1
都是如此。但这就是进入场景的地方,因为它的操作是(可扩展地)删除其参数两端的空格a
a<space>
\tl_trim_spaces:n
和以不再可扩展的版本返回结果。
让我们看一个可能的应用。你想保存用户输入的作者姓名,例如
\author{A. Uthor \\ Department of Tetrapilectomy \\ University of Somewhere}
在内部容器中,比如说\author@name
,用于你的论文类。在这种情况下,不需要可扩展性,你只需要第一个解决方案;我还添加了\author@affil
第二部分。
\ExplSyntaxOn
\RenewDocumentCommand{\author}{m}
{
% split the input at \\
\seq_set_split:Nnn \l_tmpa_seq { \\ } { #1 }
% detach the first item in the sequence
\seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl
% now define name and address
\tl_set_eq:cN { author@name } \l_tmpa_tl
\tl_set:cx { author@affil } { \seq_use:Nn \l_tmpa_seq { \\ } }
}
\ExplSyntaxOff
最后一个块中的第一条指令\author@name
与 相同\l_tmpa_tl
;第二条指令重新插入\\
剩余项之间。
答案2
您可以将\myfun
宏创建为可扩展的宏:
\def\myfun #1{\myfunA #1\\\end}
\def\myfunA #1\\#2\end{\stripspace #1\end/ \end/\next{#1}}
\def\stripspace #1 \end/#2\next#3{\ifx\end#2\end #3\else #1\fi}
% test:
\message{"\myfun{a \\ b \\ c}"} % ... prints "a"
\message{"\myfun{a}"} % ... prints "a"
假设\myfun{a // b}
。然后\myfunA a // b\end
调用并扫描到a
其#1
和。如果你接受结果(即加上空格),那么你可以简单地定义b
#2
a
a
\def\myfunA #1\\#2\end{#1}
就这些了。但是如果你想从结果中删除可选的最后一个空格,你必须定义宏\stripspace
并使用它。
我的解决方案的优点:它仅基于 TeX 原语。你不需要任何解释。而且,理解它的工作原理比学习一门新的语言\def
要好得多。\def
答案3
您指定
\\
如果省略了空格,则不会出现所需的行为,- 也不是在边缘情况下的期望行为,例如参数为
\myfun
空/空白, - 也不知道重点是排版的结果(可能是在数学模式中)还是获取一组标记(例如,用于进一步处理)——为了好玩,我假设是后者,
- 也不知道可扩展性/稳健性是否值得关注。为了好玩,我假设可扩展性值得关注。
只要\myfun
- 它的参数不包含序列
\ForBidden /
(除非嵌套在花括号中)并且 - 该标记
\ForBidden
未被定义为\outer
。
由于\romannumeral
-expansion,该机制的结果是通过触发 上的两个扩展步骤来传递的\myfun
。
该机制不需要任何\if..\else..\fi
-thingies,因此可以处理包含不平衡\if..
或\else
或 的参数\fi
。
我确保在构成机制的扩展级联运行时,参数嵌套在花括号中 -things 嵌套在花括号中确保\\
包含在 的\myfun
参数中不会干扰扩展级联期间表/对齐的收集,并且在表/对齐内您可以使用该机制提取包含&
和类似的东西。
该机制仅基于扩展,因此机制本身也应该在纯扩展上下文中发挥作用(例如,在 内\csname..\endcsname
、 内\edef
、 内\expanded
、在 -expansion 期间\romannumeral
/在扩展用于查找 TeX-⟨number⟩-quantities 的组件或 ⟨general text⟩ 的左括号的标记时,...)。
我尽了最大的努力确保不会发生不必要的花括号删除。
如果您在\edef
或类似方法中使用此机制,则确保保护/防止使用 参数提供的脆弱命令扩展取决于\myfun
您。来自 参数的标记将\myfun
在 发起的扩展级联执行过程\myfun
终止时进行处理,即,来自 参数的标记将在的工作完成\myfun
时进行处理。(在 LaTeX 中,在许多情况下,您可以通过在 前面添加或通过将宏定义为健壮命令来做到这一点,例如,就或 通过或类似方法而言。通过 定义的命令也很健壮。)\myfun
\protect
\protected
\DeclareRobustCommand
\NewDocumentCommand
\myfun
处理包含 -separated 列表的参数,\\
如下所示:
提取构成第一个元素的标记集。
如果存在,则从该标记集中删除前导和尾随显式空格标记。然后,如果存在,则删除围绕整个剩余标记集的一层花括号。这样,您可以使用一层环绕花括号来隐藏不应被删除的前导/尾随显式空格标记。
我不提供任何保证。使用风险由您自行承担。
%%-----------------------------------------------------------------------------
%% Paraphernalia
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\chardef\stopromannumeral=`\^^00
%%.............................................................................
%% Check whether argument is empty:
%%.............................................................................
%% \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>
\long\def\CheckWhetherNull#1{%
\romannumeral\expandafter\secondoftwo\string{\expandafter
\secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}\expandafter\stopromannumeral\secondoftwo}{%
\expandafter\stopromannumeral\firstoftwo}%
}%
%%-------------------------------------------------------------------------------
\long\def\trimtrailspaces#1{%
\romannumeral
\expandafter\exchange\expandafter{%
\romannumeral\trimtrailspacesloop{{}}#1\ForBidden/ \ForBidden/\ForBidden/ {{}#1}%
}\stopromannumeral
}%
\long\def\trimtrailspacesloop#1 \ForBidden/#2\ForBidden/ #3{%
\CheckWhetherNull{#2}{% no trailing space
\firstoftwo\stopromannumeral#3%
}{% trailing space
\trimtrailspacesloop#1\ForBidden/ \ForBidden/\ForBidden/ {#1}%
}%
}%
\firstoftwo{\def\gobblespace}{} {}%
\long\def\trimleadspaces#1{%
\romannumeral
\expandafter\exchange\expandafter{%
\romannumeral\trimleadspacesloop\ForBidden/#1\ForBidden/ \ForBidden/\ForBidden/{#1}%
}\stopromannumeral
}%
\long\def\trimleadspacesloop#1\ForBidden/ #2\ForBidden/\ForBidden/#3{%
\CheckWhetherNull{#1}{% Leading space
\expandafter\exchange\expandafter{\expandafter{\gobblespace#3}}%
{\trimleadspacesloop\ForBidden/#2\ForBidden/\ForBidden/}%
}{% no leading space
\stopromannumeral#3%
}%
}%
\long\def\extractfirstdoubleslashed#1{%
\romannumeral
\expandafter\exchange\expandafter{%
\romannumeral\extractdoubleslashedB{{}}#1\\\ForBidden/%
}\stopromannumeral
}%
\long\def\extractdoubleslashedB#1\\#2\ForBidden/{%
\firstoftwo\stopromannumeral#1%
}%
\long\def\removebraces#1{%
\romannumeral
\expandafter\exchange\expandafter{%
\romannumeral
\expandafter\CheckWhetherNull\expandafter{\firstoftwo#1{}{}}{\stopromannumeral#1}{%
\expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}{\expandafter\stopromannumeral\secondoftwo{}#1}{%
\stopromannumeral#1%
}%
}%
}\stopromannumeral
}%
\long\def\myfun#1{%
\romannumeral\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\stopromannumeral
\expandafter\removebraces\expandafter{%
\romannumeral\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\stopromannumeral
\expandafter\trimtrailspaces\expandafter{%
\romannumeral\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\stopromannumeral
\expandafter\trimleadspaces\expandafter{%
\romannumeral\expandafter\expandafter\expandafter\stopromannumeral
\extractfirstdoubleslashed{#1}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testA
\expandafter\expandafter\expandafter{%
\myfun{a}%
}%
\show\testA
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testB
\expandafter\expandafter\expandafter{%
\myfun{a \\ b}%
}%
\show\testB
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testC
\expandafter\expandafter\expandafter{%
\myfun{ a \\ b}%
}%
\show\testC
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testD
\expandafter\expandafter\expandafter{%
\myfun{ a\\ b}%
}%
\show\testD
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testE
\expandafter\expandafter\expandafter{%
\myfun{a \\ b \\ c}%
}%
\show\testE
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testF
\expandafter\expandafter\expandafter{%
\myfun{$\theta$ \\ b \\ c}%
}%
\show\testF
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testG
\expandafter\expandafter\expandafter{%
\myfun{ \fi unbalanced \\ b \\ c}%
}%
\show\testG
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testH
\expandafter\expandafter\expandafter{%
\myfun{}%
}%
\show\testH
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testI
\expandafter\expandafter\expandafter{%
\myfun{ }%
}%
\show\testI
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testJ
\expandafter\expandafter\expandafter{%
\myfun{ \\ }%
}%
\show\testJ
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testK
\expandafter\expandafter\expandafter{%
\myfun{\\}%
}%
\show\testK
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\testL
\expandafter\expandafter\expandafter{%
\myfun{ { $\theta$ } \\ b \\ c}%
}%
\show\testL
\bye
该示例在终端上生成以下消息:
> \testA=macro:
->a.
l.122 \show\testA
?
> \testB=macro:
->a.
l.129 \show\testB
?
> \testC=macro:
->a.
l.136 \show\testC
?
> \testD=macro:
->a.
l.143 \show\testD
?
> \testE=macro:
->a.
l.150 \show\testE
?
> \testF=macro:
->$\theta $.
l.157 \show\testF
?
> \testG=macro:
->\fi unbalanced.
l.164 \show\testG
?
> \testH=macro:
->.
l.171 \show\testH
?
> \testI=macro:
->.
l.178 \show\testI
?
> \testJ=macro:
->.
l.185 \show\testJ
?
> \testK=macro:
->.
l.192 \show\testK
?
> \testL=macro:
-> $\theta $ .
l.199 \show\testL