我想要做的几件事非常相似,基本上涉及将宏扩展保留到最后一刻。
可能最简单的例子是,我有一组宏,它们扩展为“X 中的对象”的变体,但有时我会覆盖默认值。因此,while\cobj
扩展为“C 中的对象”,\sobj
扩展为“集合”,\gobj
扩展为“组”。当我想谈论一个对象。一般来说,我应该输入“an \cobj”,但在特定情况下,我可能需要更改为“a \sobj”。困难在于,使用这些宏的一个原因是我可以改变主意,确定哪些宏有特殊名称,哪些没有。但如果我必须重新将所有“an”重写为“a”,那么(部分)目的就落空了。所以我想做的是定义一个宏“\a”,检查下一个字符是否为元音,并适当地生成“an”或“a”(模数奇数特殊情况!)。
困难之处在于它后面不是字符,而是宏。所以我需要扩展该宏来找出最终的第一个字符。如果它是一个简单的宏,那么\def\cobj{object in C}
我只需执行\def\a{\noexpand\@a}
和\def\@a{\@ifnextchar{a}{an}{\@ifnextchar{e}{an}...}
,但\cobj
它是一个相当复杂的宏,在实际到达最终字符之前需要扩展几个级别。特别是,\cobj
接受一个可选参数。
所以我希望能够\a
在最后一刻进行扩展。有点像\noexpand
,但这只会对一跳进行不扩展,而不是“结束 - 1”跳。
有人知道该怎么做吗?为了表明可以接受的答案水平,我知道,\expandafter
而且我不怕使用它!
编辑:这是另一个稍微复杂一点的例子。在与上述情况类似的情况下,我有\dcat
一个“类别 D”的例子。在少数情况下,我想“吃掉”“the”,所以我想要一个\nothe
可以预先设置的命令:\nothe\dcat
它会吃掉它后面的第一个单词。编写一个命令来吞掉下一个单词很简单输入流,但我想要输出消失。
答案1
另一个回答可以解决您的问题,但不能解决您的问题,那么创建一个“\newobject”命令怎么样?您可以在其中指定它是“a”还是“an”(然后您就不会遇到任何异常!)并为您提供一组命令以根据需要进行排版。例如
\newobject{Gobj}{a}{group}
\newobject{Aobj}{an}{axiom}
\Gobj % group
\aGobj % a group
\theGobj % the group
\Aobj % axiom
\aAobj % an axiom
\theAobj % the axiom
当然,您可以定义替代接口,例如\A\GObj
或\The{gobj}
或任何对您来说更好的接口。
答案2
编译时,下面的文件会生成两个包,然后在下面的测试文件中使用它们。
\documentclass{article}
\usepackage{nextword}
\ExplSyntaxOn
\newcommand { \nothe }
{ \nextword_do:n { \str_if_eq:xxF {\g_nextword_tl} {the} {\g_nextword_tl} } }
\ExplSyntaxOff
\begin{document}
\newcommand{\obj}[1][the default]{#1 object}
\nothe Hello, \unskip\nothe the World!
\nothe \obj [Any] is nice, including\nothe\obj s.
\end{document}
第一个包提供了“扩展”完整 token 的工具,在此过程中执行一些 TeX 原语。这使我们能够接近 TeX 的输出。在此基础上构建包nextword
,它在输出时抓取以下单词(当前“单词”在下一个空格处停止,将其留在输出中)。单词保存在 中。如果不是 ,则测试文件中定义的\g_nextword_tl
命令会将其放回。\nothe
\g_nextword_tl
the
\RequirePackage{filecontents}
\begin{filecontents}{sex.sty}
%% Package sex "Strong expansion for LaTeX"
\RequirePackage{expl3}
\ProvidesExplPackage{sex}{2011/07/12}{v<3}{Strong expansion for LaTeX}
\tl_new:N \g_sex_tl
\cs_new:Npn \sex:nf #1
{
\tl_gset:Nn \g_sex_tl {#1}
\sex_aux:
}
\cs_new:Npn \sex_aux:
{
\group_begin:
\int_set:Nn \tex_escapechar:D { -1 }
\group_align_safe_begin:
\sex_peek:
}
\cs_new:Npn \sex_peek: { \peek_after:Nw \sex_test: }
\cs_new:Npn \sex_test:
{
\token_if_expandable:NTF \l_peek_token
{ \exp_after:wN \sex_peek: }
{
\cs_if_exist:cTF
{ sex_ \token_to_meaning:N \l_peek_token : }
{
\group_align_safe_end:
\exp_args:Nc \group_end:
{ sex_ \token_to_meaning:N \l_peek_token : }
}
{
\token_if_group_begin:NTF \l_peek_token
{ \sex_bgroup: }
{
\token_if_group_end:NTF \l_peek_token
{ \sex_egroup: }
{
\token_if_eq_meaning:NNTF \l_peek_token \c_space_token
{ \sex_space: }
{ \sex_stop: }
}
}
}
}
}
\cs_new:Npn \sex_stop:
{
\group_align_safe_end:
\group_end:
\g_sex_tl
}
% Execute the following token (which may be {, ,}) before "\sex_aux".
\cs_new:Npn \sex_swap:
{
\group_align_safe_end:
\group_end:
\tex_afterassignment:D \sex_swap_aux:
\cs_gset_eq:NN \g_sex_token
}
\cs_new:Npn \sex_swap_aux: { \g_sex_token \sex_aux: }
\cs_new_eq:NN \sex_bgroup: \sex_swap:
\cs_new_eq:NN \sex_egroup: \sex_swap:
\cs_new_eq:NN \sex_space: \sex_swap:
\cs_new_eq:NN \sex_begingroup: \sex_swap:
\cs_new_eq:NN \sex_endgroup: \sex_swap:
\cs_new_eq:NN \sex_relax: \sex_swap:
% Assignments: do the assignment, then regain control.
\cs_new:Npn \sex_assignment:
{ \tex_afterassignment:D \sex_aux: }
\cs_new_eq:NN \sex_let: \sex_assignment:
\cs_new_eq:NN \sex_futurelet: \sex_assignment:
\cs_new_eq:NN \sex_def: \sex_assignment:
\cs_new_eq:NN \sex_edef: \sex_assignment:
\cs_new_eq:NN \sex_gdef: \sex_assignment:
\cs_new_eq:NN \sex_xdef: \sex_assignment:
\cs_new_eq:NN \sex_long: \sex_assignment:
\cs_new_eq:NN \sex_global: \sex_assignment:
\cs_new_eq:NN \sex_protected: \sex_assignment:
\cs_new_eq:NN \sex_escapechar: \sex_assignment:
\cs_new_eq:NN \sex_endlinechar: \sex_assignment:
\cs_new_eq:NN \sex_catcode: \sex_assignment:
% Document command
\cs_new_eq:NN \StrongExpand \sex:nf
\end{filecontents}
\begin{filecontents}{nextword.sty}
%% Package nextword "Catch the next word in the output."
\RequirePackage{sex}
\ProvidesExplPackage{nextword}{2011/07/12}{v0}{Next word of output}
\tl_new:N \g_nextword_tl
\tl_new:N \g_nextword_action_tl
\cs_new_protected:Npn \nextword_do:n #1
{
\tl_gset:Nn \g_nextword_action_tl {#1}
\tl_gclear:N \g_nextword_tl
\sex:nf
{
\cs_gset:Npn \sex_space:
{
\cs_gset_eq:NN \sex_space: \sex_swap:
\tl_gset_eq:NN \g_sex_tl \g_nextword_action_tl
\sex_stop:
}
\nextword_loop:N
}
}
\cs_new_protected:Npn \nextword_loop:N #1
{
\token_if_cs:NTF #1
{
\cs_gset_eq:NN \sex_space: \sex_swap:
\g_nextword_action_tl
#1
}
{
\tl_put_right:Nx \g_nextword_tl
{
\exp_after:wN \nextword_meaning_to_char:w
\token_to_meaning:N #1
}
\sex:nf { \nextword_loop:N }
}
}
\cs_new:Npn \nextword_meaning_to_char:w #1 ~ #2 ~ %
{
\prg_case_str:nnn {#1}
{
{math} { \nextword_meaning_to_char_aux:w }
{alignment} { \nextword_meaning_to_char_aux:w }
{macro} { \nextword_meaning_to_char_aux:w }
}
{ }
}
\cs_new:Npn \nextword_meaning_to_char_aux:w #1 ~ { }
\end{filecontents}
% ===== Test file.
\documentclass{article}
\usepackage{nextword}
\ExplSyntaxOn
\newcommand { \nothe }
{ \nextword_do:n { \str_if_eq:xxF {\g_nextword_tl} {the} {\g_nextword_tl} } }
\ExplSyntaxOff
\begin{document}
\newcommand{\obj}[1][the default]{#1 object}
\nothe Hello, \unskip\nothe the World!
\nothe \obj [Any] is nice, including\nothe\obj s.
\end{document}
答案3
如今,不使用 eTeX 已经没有什么意义了(所有扩展的 TeX 变体,如 PDFTeX、LuaTeX 或 XeTeX 都包含它),并且用\protected\def
及其变体定义的宏只有在使用 或显式扩展时才会扩展\expandafter
。在 writes 和 edefs 内部,它们保持不变。当然,这不会扩展到它们的任何参数,因为在宏实际扩展之前,它们不被视为参数。
答案4
完全扩展具有非固定数量参数的东西是相当困难的,至少除非你知道足够多的知识来自己掌握它们。原因是,就 TeX 而言,你的“最后可能扩展时刻”是当读取 token 时。TeX 知道,你\a
可以改变后续宏的含义、类别代码、ETC。
您能否更详细地介绍一下可选参数的性质。可以有一个完全可扩展的宏,它接受可选参数(请参阅 xparse 了解一种方法),因此您可以想象设计它,\a
以便它查找可选参数,在适当的情况下抓取它,执行\edef
for\cobj
并最终执行您想要执行的操作。