定义 \myfun,得到 \myfun{a \\ b} = a 和 \myfun{a} = a

定义 \myfun,得到 \myfun{a \\ b} = a 和 \myfun{a} = a

我如何定义一个\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名称中的部分表示该函数具有“奇怪的”参数。

因此,在 和 的情况下aa \\ 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都是如此。但这就是进入场景的地方,因为它的操作是(可扩展地)删除其参数两端的空格aa<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#2aa

\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

相关内容