\foo{…}
我仍然不明白(也没有想过)将参数设为或\foo\bgroup …\egroup
(甚至不平衡的\bgroup …}
和)的利弊{…\egroup
。但我很好奇是否可以为某些命令启用它(例如,您可以通过命令创建环境)。
我们如何创建这样的宏
\def\foo#1{…}
接受
\foo\bgroup …\egroup
我想到另一个选择是像这样的命令
\delimitedbybegroup{<pre code>}<argument delimited by braces or \bgroup and \egroup>
因此,人们可以说,\delimitedbybegroup\emph{Some text}
或者\delimitedbybegroup\emph\bgroup Some text\egroup
他们双方都给予\emph{Some text}
。
例如,更高级的方法可能是
\delimitedbybegroup{\def\foo#1#2}\bgroup Something with #1 and #2\egroup
无论如何,任何关于为什么以及为什么不有用的想法,这种命令都是受欢迎的。
类似地,如果 token 不可见,那么(通常)如何处理那些确实寻找“结束”token 的命令。比如
\def\foo{\delimiter}
\def\baz#1\delimiter{…#1…}
\baz some code \foo{} and some more here that should be outside the argument of \string\baz.
答案1
如果从问题的结尾来看,这个问题会给我一种感觉:可以创建一个在参数扫描期间扩展其参数的宏. 那么变体}
或\egroup
作为参数的分隔符是可用的。
我\eparam
用以下语法创建了宏:
\def\mymacro #1{the #1 parameter is declared as undelimited}
...
\eparam\mymacro parameter-text
等于被括号括起来的,或者被另一个由和声明的控制序列parameter-text
括起来的。 例如:real-parameter-text
\eparamopen
\eparamclose
\eparamopen\start \eparamclose\stop
\eparam\mymacro {real-parameter-text}
\eparam\mymacro \start real-parameter-text\stop
\eparam\mymacro \start real-parameter-text}
\eparam\mymacro {real-parameter-text\stop
其要点\eparam
是准备一个扩展参数。real-parameter-text
在参数扫描期间会像 一样展开\edef
。这意味着在读取参数期间会展开所有可扩展原语和宏。此时不可扩展原语不执行任何操作(如\edef
),因此您可以重新分配此参数内的寄存器/宏,但不会对参数扫描产生任何影响。这是这种情况与\hbox {...}
原语语法之间的主要区别。
和参数扫描之间有一个小区别\edef
:未定义的控制序列在参数扫描期间不执行任何操作(如不可扩展的原语)。只有当使用该参数时才会发生错误(在参数扫描期间不会发生)。
声明的分隔符\eparamclose
可以在宏中隐藏。例如:
\def\x{-text\stop}
\eparam\mymacro {real-parameter\x
给出的第一个左括号或分隔符\eparamopen
是可选的。即您可以省略它:
\eparam\mymacro real-parameter-text\stop
参数始终通过括号匹配。这意味着声明的分隔符\eparamclose
在内部括号对内不起作用(与正常参数扫描一样):
\eparam \start text{inside \stop braces}text\stop
% the parameter is: "text{inside \stop braces}text"
接下来是实施(或者 wipet 的魔法:)和小测试。
\def\tmp{% all expandable primitives (only from classical TeX, you can add others):
\botmark \csname \else \endcsname \endinput \expandafter \fi \firstmark \fontname
\if \ifcase \ifcat \ifdim \ifeof \iffalse \ifhbox \ifhmode \ifinner
\ifmmode \ifnum \ifodd \iftrue \ifvbox \ifvmode \ifvoid \ifx
\input \jobname \meaning \noexpand \number \or \romannumeral
\splitbotmark \splitfirstmark \string \the \topmark
}
\def\skipmm#1->{} \def\showmm#1->{#1}
\edef\textmm{\expandafter\showmm\meaning\empty}
\edef\expandprimitives{\expandafter\skipmm\meaning\tmp}
\def\isinlist#1#2#3{% from opmac.tex
\def\tmp##1#2##2\end{\def\tmp{##2}%
\ifx\tmp\empty \csname iffalse\expandafter\endcsname \else
\csname iftrue\expandafter\endcsname \fi}% end of \def\tmp
\expandafter\tmp#1\endlistsep#2\end
}
\def\isexpanded#1#2{% \isexpanded X\iftrue the X is expandable primitive or macro\fi
\edef\tmpb{\meaning#1\space}%
\expandafter\isinlist\expandafter\tmpb\expandafter{\textmm}%
\iftrue \csname iftrue\expandafter\endcsname\else
\def\nexxt{\expandafter\isinlist\expandafter\expandprimitives\expandafter{\tmpb}.}%
\expandafter\nexxt\fi
}
\def\eparamopen#1{\def\eparamopenA{\let#1=\eparamopenA}}
\def\eparamclose#1{\def\eparamcloseA{\let#1=\eparamcloseA}}
\newtoks\eparamT
\def\eparam#1{\begingroup
\toks0={#1}\let\bgroup=\relax \let\egroup=\relax
\let\ifIamInGroup=\iffalse
\ifx\eparamopenA\undefined \def\eparamopenA{^\eparam^}\else \eparamopenA\fi
\ifx\eparamcloseA\undefined \def\eparamcloseA{^\eparam^}\else \eparamcloseA\fi
\eparamT={}\eparamA
}
\def\eparamA{\futurelet\tmpc\eparamB}
\def\eparamB{\let\next=\eparamD
\isexpanded\tmpc\iftrue \def\next{\expandafter\eparamA}\fi
\ifx\tmpc\bgroupOri \let\next=\eparamC \let\nexxt=\eparamD \fi
\ifx\tmpc\eparamopenA \let\next=\eparamC \let\nexxt=\eparamD \fi
\next
}
\def\eparamC{\afterassignment\nexxt \let\next= }
\def\eparamD{\futurelet\tmpc\eparamE}
\def\eparamE{\let\next=\eparamN
\isexpanded\tmpc\iftrue \def\next{\expandafter\eparamD}\fi
\ifx\tmpc\spacetoken \let\next=\eparamC \let\nexxt=\eparamD \eparamX{ }\fi
\ifx\tmpc\eparamcloseA \ifIamInGroup \let\next=\eparamN
\else \let\next=\eparamC \let\nexxt=\eparamF \fi\fi
\ifx\tmpc\egroupOri \let\next=\eparamC \let\nexxt=\eparamF \fi
\ifx\tmpc\bgroupOri \let\next=\eparamC \let\nexxt=\eparamG \fi
\next
}
\def\eparamN#1{\eparamX#1\eparamD}
\def\eparamG{\begingroup \let\ifIamInGroup=\iftrue \eparamT={}\eparamD}
\def\eparamF{\ifIamInGroup \let\next=\eparamY \else \let\next=\eparamZ \fi \next}
\long\def\eparamX#1{\eparamT\expandafter{\the\eparamT#1}}
\def\eparamY{\expandafter\endgroup
\expandafter\eparamT\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\eparamT\expandafter{\the\eparamT}}%
\eparamD
}
\def\eparamZ{\expandafter\endgroup\the\toks0\expandafter{\the\eparamT}}
\let\bgroupOri=\bgroup
\let\egroupOri=\egroup
\def\tmp/{\let\spacetoken= }\tmp/ %
\def\macro#1{\toks0={#1}\message{the parameter is "\the\toks0"}}
\eparam\macro {abc} % the parameter is "abc"
\def\x{ww}
\eparam\macro {ab\x c} % the parameter is "abwwc"
\eparam\macro {ab\the\pageno c} % the parameter is "ab1c"
\eparam\macro {ab\ifx\x\x true\else false\fi c} % the parameter is "abtruec"
\eparam\macro {ab\ifnum\folio=1 true\else false\fi c} % the parameter is "abtruec"
\eparam\macro {ab\ifcase\pageno oo\or one\or two\fi c} % the parameter is "abonec"
\eparamopen\start \eparamclose\stop
\eparam\macro {abc\stop % the parameter is "abc"
\eparam\macro \start abc\stop % the parameter is "abc"
\eparam\macro \start abc} % the parameter is "abc"
\eparam\macro abc} % the parameter is "abc"
\eparam\macro abc\stop % the parameter is "abc"
\eparam\macro \start abc{uf\stop fu}ee\stop % the parameter is "abc{uf\stop fu}ee"
\def\y{end\stop}
\eparam\macro {a\x\y % the parameter is "awwend"
\end
编辑:为了解决问题的最后一个需求,我确实对代码做了一些小的修改:
\eparamopen\bgroup \eparamclose\egroup
\eparam {\def\foo#1#2}\bgroup Something with #1 and #2\egroup
就像\edef\foo#1#2{Something with #1 and #2}
。
如果您需要停用扩展过程(即您需要执行\def
,否\edef
),那么您可以声明
\def\isexpanded#1#2{\iffalse}
当然,\eparamclose
这种情况下,在嵌套宏中无法找到声明的分隔符。
答案2
TeX 的语法规则允许某些基元具有由\bgroup
或分隔的“参数” \egroup
。主要示例包括
\hbox
,\vbox
,\vtop
,\vcenter
,\halign
和\noalign
。
\halign
这是LaTeX 的基础tabular
:将\begin{tabular}{<arg>}
论证转化为合适的序言\halign
,然后,除了与本讨论无关的其他内容外,TeX 执行
\leavevmode$\vcenter\bgroup\halign\bgroup<built preamble>\cr
并且,当\end{tabular}
被发现时,
\crcr\egroup\egroup$
执行。\vcenter
可以是\vbox
或 ,\vtop
具体取决于 给出的选项tabular
。当然, 中也利用了这种可能性lrbox
。
其他原始命令接受形式为的参数<general text>
,TeXbook 将其描述为
<filler>{<balanced text><right brace>
其中是和空间标记<filler>
的任意序列,意味着\relax
{
类别代码为 1 的隐式或显式标记(所以要么{
或\bgroup
)并且<right brace>
意味着类别代码为 1 的显式字符标记。
除了分配给令牌寄存器之外,具有此特征的原语示例还有\uppercase
,,和:\message
\write
\mark
<token variable><equals><general text>
那么你不能定义一个基于\uppercase
其参数以 分隔的宏\egroup
,但您可以使用 来做到这一点\hbox
。
更糟糕的是,对于你的计划来说,宏定义中的替换文本必须分别由第 1 类和第 2 类的明确字符标记来定界。
请注意,有一条经验法则可以判断哪些基元可以接受\egroup
为最终分隔符:参数包含在隐式组中的基元(关于 有点奇怪,\halign
因为赋值将在此隐式组之外执行)。顺便说一下,对于和\tabskip
的基元操作也是如此。_
^
不可能定义一个通用宏,其参数可以用}
或分隔\egroup
。一个明显的参数可以,前提是替换文本\egroup
首先以原始接受结尾;然后可以使用诡计来完成任务。举一个愚蠢的例子,下面是一个将命名颜色作为可选参数\aftergroup
的实现:\vfbox
\documentclass{article}
\usepackage{color}
\makeatletter
\newcommand{\vfbox}[1][black]{%
\def\vfbox@color{#1}%
\setbox\vfbox@box=\hbox\bgroup
\aftergroup\vfbox@do
\let\next=
}
\newcommand{\vfbox@do}{%
\begingroup
\color{\vfbox@color}%
\fbox{\box\vfbox@box}%
\endgroup
}
\newbox\vfbox@box
\makeatother
\begin{document}
\vfbox\bgroup abc\egroup
\vfbox{abc\egroup
\vfbox[blue]\bgroup abc}
\end{document}
但是你不能用它做到这一点\uppercase
(除了它的限制之外)。
答案3
我知道这个问题引起了争论,有些用户认为它不直接也不实用。然而,正如我说在问题中,这是出于好奇,所以 wipet 的解决方案确实有效。
但是,我倾向于理解更容易理解的 expl3 代码,而不是仅仅理解 TeX 原语(例如,这里使用的许多宏都是通过逻辑找到的,在文档中搜索我认为是我正在寻找的命令的正确名称,然后我找到了它们)。
这或多或少是 wipet 的答案,但试图通过 expl3 带来一点清晰度。我做过改变一些东西,所以可能不起作用;它只是似乎有效并给出例子。
我根本没有尝试为这些函数寻找更好的名称(看看我的懒惰,甚至在@@
函数名称中使用以避免“发明”某些东西,或者:w
到处使用以避免思考)。我没有找到一种只使用标记列表而不是弃用的toks的方法(虽然我尝试过,但我在这方面相当缺乏经验)。我甚至可能由于缺乏知识而误用了expl3。所以,任何事物受到欢迎。
这里是。
\documentclass[parskip=full,dvipsnames,x11names]{scrartcl}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
%\usepackage[mathlf]{MinionPro}
\usepackage{xparse,xcolor,etoolbox,multicol}
\makeatletter
\ExplSyntaxOn
% From l3toks (begin)
\cs_new_protected:Npn \toks_new:N #1
{ \__chk_if_free_cs:N #1 \newtoks #1 }
\toks_new:N \c_empty_toks
\cs_new_eq:NN \toks_use:N \tex_the:D
\cs_new_protected_nopar:Npn \toks_set_eq:NN #1 #2 { #1 = #2 }
\cs_new_protected:Npn \toks_set:Nn #1 #2 { #1 = { #2 } }
\cs_new_protected:Npn \toks_set:No #1 #2 { #1 = \exp_after:wN { #2 } }
\cs_new_protected:Npn \toks_gset:Nn { \tex_global:D \toks_set:Nn }
\cs_new_protected:Npn \toks_gset:No { \tex_global:D \toks_set:No }
\cs_new_protected_nopar:Npn \toks_clear:N #1
{ \toks_set_eq:NN #1 \c_empty_toks }
\cs_new_protected:Npn \toks_put_right:Nn #1 #2
{ #1 = \exp_after:wN { \toks_use:N #1 #2 } }
\cs_generate_variant:Nn \toks_set:Nn { Nx }
\cs_generate_variant:Nn \toks_gset:Nn { Nx }
\toks_new:N \g_tmpa_toks
% (end) % check if correct and drop this and use _tl if possible
\NewDocumentCommand \egrabparameter { }
{
\tl_set_eq:NN \l_@@_macro_noexpand_tl \g_@@_macros_noexpand_tl
\bool_set_true:N \l_@@_expand_bool
\@@_grabparameter:nw
}
\NewDocumentCommand \grabparameter { }
{
\tl_set_eq:NN \l_@@_macro_noexpand_tl \g_@@_macros_noexpand_tl
\bool_set_false:N \l_@@_expand_bool
\@@_grabparameter:nw
}
\cs_new_protected_nopar:Npn \@@_set_begindelim:N #1
{ \cs_set:Npn \@@_begin_delim: { \cs_set_eq:NN #1 \@@_begin_delim: } }
\cs_new_protected_nopar:Npn \@@_set_enddelim:N #1
{ \cs_set:Npn \@@_end_delim: {\cs_set_eq:NN #1 \@@_end_delim: } }
\cs_new_protected_nopar:Npn \@@_set_to_relax:N #1
{ \cs_set_eq:NN #1 \scan_stop: }
\toks_new:N \l_@@_parameter_toks
\toks_new:N \l_@@_preamble_toks
\bool_new:N \l_@@_ingroup_bool
\bool_new:N \l_@@_expand_bool
\tl_new:N \g_@@_macros_noexpand_tl
\tl_new:N \l_@@_macros_noexpand_tl
\tl_new:N \l_@@_parameter_tl
\tl_new:N \l_@@_preamble_tl
\cs_new_protected:Npn \@@_grabparameter:nw #1
{
\group_begin:
\tl_map_function:NN \g_@@_macros_noexpand_tl \@@_set_to_relax:N
\toks_set:Nn \l_@@_preamble_toks { #1 }
\cs_set_eq:NN \bgroup \scan_stop:
\cs_set_eq:NN \egroup \scan_stop:
\bool_set_false:N \l_@@_ingroup_bool
\bool_if:NF \l_@@_expand_bool
{ \cs_set_eq:NN \token_if_expandable:NT \use_none:nn }
\cs_if_exist:NTF \@@_begin_delim:
{ \@@_begin_delim: }
{ \cs_set:Npn \@@_begin_delim: { ^\eparam^ } }
\cs_if_exist:NTF \@@_end_delim:
{ \@@_end_delim: }
{ \cs_set:Npn \@@_end_delim: { ^\eparam^ } }
\toks_clear:N \l_@@_parameter_toks
\@@_aux_A:w
}
\cs_new_protected_nopar:Npn \@@_aux_A:w { \peek_after:Nw \@@_aux_B:w }
\cs_new_protected_nopar:Npn \@@_aux_B:w
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_D:w
\token_if_expandable:NT \l_peek_token
{ \cs_set:Npn \__tmpa_cs:w { \exp_after:wN \@@_aux_A:w } }
\token_if_group_begin:NT \l_peek_token
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w
}
\token_if_eq_meaning:NNT \l_peek_token \@@_begin_delim:
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w
}
\__tmpa_cs:w
}
\cs_new_protected_nopar:Npn \@@_aux_C:w
{
\tex_afterassignment:D \__tmpb_cs:w
\cs_set_eq:NN \__tmpa_cs:w
}
\cs_new_protected_nopar:Npn \@@_aux_D:w { \peek_after:Nw \@@_aux_E:w }
\cs_new_protected_nopar:Npn \@@_aux_E:w
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_N:w
\token_if_expandable:NT \l_peek_token
{ \cs_set:Npn \__tmpa_cs:w { \exp_after:wN \@@_aux_D:w } }
\token_if_space:NT \l_peek_token
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_D:w
\@@_aux_X:w { ~ }
}
\token_if_eq_meaning:NNT \l_peek_token \@@_end_delim:
{
\bool_if:NTF \l_@@_ingroup_bool
{ \cs_set_eq:NN \__tmpa_cs:w \@@_aux_N:w }
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_F:w
}
}
\token_if_group_end:NT \l_peek_token
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_F:w
}
\token_if_group_begin:NT \l_peek_token
{
\cs_set_eq:NN \__tmpa_cs:w \@@_aux_C:w
\cs_set_eq:NN \__tmpb_cs:w \@@_aux_G:w
}
\__tmpa_cs:w
}
\cs_new_protected:Npn \@@_aux_N:w #1 { \@@_aux_X:w #1 \@@_aux_D:w }
\cs_new_protected_nopar:Npn \@@_aux_G:w
{
\group_begin:
\bool_set_true:N \l_@@_ingroup_bool
\toks_clear:N \l_@@_parameter_toks
\@@_aux_D:w
}
\cs_new_protected_nopar:Npn \@@_aux_F:w
{
\bool_if:NTF \l_@@_ingroup_bool
{ \cs_set_eq:NN \__tmpa_cs:w \@@_aux_Y:w }
{ \cs_set_eq:NN \__tmpa_cs:w \@@_aux_Z:w }
\__tmpa_cs:w
}
\cs_new_protected:Npn \@@_aux_X:w #1
{
\toks_put_right:Nn \l_@@_parameter_toks { #1 }
}
\cs_new_protected_nopar:Npn \@@_aux_Y:w
{
% \exp_after:wN \group_end: % -
% \exp_after:wN \l_@@_parameter_toks % -
% \exp_after:wN \exp_after:wN % -
% \exp_after:wN { % -
% \exp_after:wN \toks_use:N % -
% \exp_after:wN \l_@@_parameter_toks % -
% \exp_after:wN { % -
% \toks_use:N \l_@@_parameter_toks } } % -
\toks_gset:Nx \g_tmpa_toks { \toks_use:N \l_@@_parameter_toks } % +
\group_end: % +
\toks_set:Nx \l_@@_parameter_toks % +
{ \toks_use:N \l_@@_parameter_toks { \toks_use:N \g_tmpa_toks } } % +
\@@_aux_D:w
}
\cs_new_nopar:Npn \@@_aux_Z:w
{
% \exp_after:wN \group_end: % -
% \toks_use:N \toks0 % -
%% \exp_after:wN \exp_after:wN % +
%% \exp_after:wN \group_end: % +
%% \exp_after:wN \toks_use:N % +
%% \exp_after:wN \l_@@_preamble_toks % +
%% \exp_after:wN {
%% \toks_use:N \l_@@_parameter_toks }
\tl_gset:Nx \g_tmpa_tl
{ \toks_use:N \l_@@_preamble_toks { \toks_use:N \l_@@_parameter_toks } }
\group_end:
\g_tmpa_tl
}
\@@_set_begindelim:N \bgroup
\@@_set_enddelim:N \egroup
\tl_gput_right:Nn \g_@@_macros_noexpand_tl { }
\ExplSyntaxOff
\makeatother
\begin{document}
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
\begin{multicols}{3}\KOMAoptions{parskip=never}\parindent=0pt
\def\tmp#1{[{#1}]}
\def\macro{\grabparameter\tmp}
\def\emacro{\egrabparameter\tmp}%
\macro\bgroup abc \textit{\color{Tomato3} foo} no\egroup
\macro{real-parameter-text}\par
\macro\bgroup real-parameter-text\egroup\par
\macro\bgroup real-parameter-text}\par
\macro{real-parameter-text\egroup\par
\macro{abc}
\def\x{ww}\par
\emacro{ab\x c}\par
\emacro{ab\thepage c}\par
\emacro{ab\ifx\x\x true\else false\fi c}\par
\emacro{ab\ifnum\value{page}=1 true\else false\fi c}\par
\emacro{ab\ifcase\value{page} oo\or one\or two\fi c}\par
\emacro{abc\egroup\par
\emacro\bgroup abc\egroup\par
\emacro\bgroup abc}\par
\emacro abc}\par
\emacro abc\egroup\par
\emacro\bgroup abc{\noexpand\color{red}uf\string^ fu}ee\egroup
\end{multicols}\unskip
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
\newenvironment{texttolowercase}{\egrabparameter\lowercase\bgroup}{\egroup}
\begin{texttolowercase}
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
\end{texttolowercase}
\end{document}