我正在寻找一种可以让我在组外“广播”宏的方法。具体示例包括 中的路径和范围tizpictures
。这是一个 M(N)WE。
\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\makeatletter
\let\smuggleoutone\pgfmath@smuggleone
\makeatother
\begin{document}
\begin{tikzpicture}[globalize/.code n args={2}{\xdef#2{#1}},
localize/.code n args={2}{\pgfmathsetmacro{#2}{#1}\typeout{#2}
%\smuggleoutone#1
}]
\begin{scope}[local bounding box=extra]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
\pgfextra{\xdef\myangle{\n1}};
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (extra.north) {using \verb|\pgfextra|};
%
\begin{scope}[local bounding box=globalize,xshift=3cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[globalize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (globalize.north) {using \texttt{globalize}};
%
\xdef\myangle{7}
\begin{scope}[local bounding box=localize,xshift=6cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[localize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (localize.north) {attempt to smuggle};
%
\end{tikzpicture}
\end{document}
左边的两个选项部分实现了我想要的功能,即\myangle
在路径外广播宏。但是,它们以牺牲\myangle
全局性为代价来实现这一点。钾Z 有一些内部命令可以避免这种情况,只需将宏偷运到路径之外即可。具体来说,@DavidCarlisle 建议在聊天中使用pgfmath@smuggleone
。但是,我的上述尝试失败了,即如果我取消注释
%\smuggleoutone#1
代码产生错误。
问题:是否可以将宏偷运到组外而不使其成为全局的?
“奖金”:当然,如果能解释一下所有走私命令的作用就太好了。
“奖金”: 可以想象这些方法可能独立于 Ti钾Z,所以如果有办法不让它们依赖 Ti钾Z 正在加载,这会很棒,但肯定不是必需的。
答案1
您可以使用 TeX 原语将定义从其组中“偷运”出来\aftergroup
。我将首先解释它的作用,然后给出使用\aftergroup
的一个可能定义,最后将其应用于您的 MWE。\smuggleone
\aftergroup
简短的回答是,你可以定义\smuggleone
(我已从名称中删除“out”)为
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
如果您粘贴此定义并在 MWE 中替换\smuggleoutone#1
为,它应该可以工作。(请注意,您将错误的参数传递给了,它应该是而不是。)\smuggleone#2
\smuggleoutone
#2
#1
关于\aftergroup
:
可以使用 在当前组末尾插入单个标记\aftergroup<token>
。您一次只能偷运出一个标记,因此如果您想移出由多个标记组成的东西(例如定义),则需要\aftergroup
分别移出每个标记。这包括括号({}
),例如
{\aftergroup\def\aftergroup\abc\aftergroup{\aftergroup A\aftergroup B\aftergroup C\aftergroup}}
相当于{}\def\abc{ABC}
。
这相当麻烦,因此下面的方法可能更实用:
{\gdef\somethingunique{\def\abc{ABC}}\aftergroup\somethingunique}
通过在组末尾全局分配并插入 来实现。如果 ABC 被某个宏替换,比如说\def\abc{ABC}
,该宏仅在当前组中定义,并且您希望完全展开,那么您需要改用:\somethingunique
\ABC
\xdef
{%
\newcommand*\ABC{ABC}%
\xdef\somethingunique{\def\noexpand\abc{\ABC}}%
\aftergroup\somethingunique
}
\noexpand
我在前面插入了,\abc
因为我们不想\abc
展开。如果你只想\ABC
展开一次,你可以使用稍微复杂一点的
{
\newcommand*\ABC{\somethingthatshouldntbeexpanded}%
\xdef\somethingunique{\def\noexpand\abc{\unexpanded\expandafter{\ABC}}}%
\aftergroup\somethingunique
}
(原语\noexpand
和\unexpanded
均\expandafter
在此解释这个答案。)
要将 的定义偷偷\abc
带出组,您可以执行我上面所做的操作,用本身\ABC
替换\abc
。 这样,\abc
在组结束后, 会立即将其定义为其本身(扩展一次)。
\AfterGroup
包中也有etextools
。它的作用与 大致相同\aftergroup
,但它接受一个可由任意数量的标记组成的参数。因此,例如,在当前组之后\Aftergroup{\def\abc{ABC}}
插入\def\abc{ABC}
,而没有上述所有麻烦。还有一个带星号的版本,\Aftergroup*
,它执行相同的操作,但首先完全扩展其参数。
etextools
但不要使用该包!它显然存在错误,不再维护,并且与许多其他软件包不兼容。(感谢 Ulrike Fischer 指出这一点,以下是几个示例:1,2,3,4。
即使你不应该使用该包,\AfterGroup
它本身也非常有用。它的定义如下:
\makeatletter %% <- make @ usable in command names
\newcount\ettl@fter
\newrobustcmd\AfterGroup{\@ifstar{\ettl@AfterGroup\@firstofone}{\ettl@AfterGroup\unexpanded}}
\newrobustcmd\ettl@AfterGroup[2]{%
\csxdef{ettl@fterGroup\number\numexpr\the\ettl@fter+1}%
{\global\csundef{ettl@fterGroup\number\numexpr\the\ettl@fter+1}#1{#2}}%
\global\advance\ettl@fter\@ne
\expandafter\aftergroup\csname ettl@fterGroup\the\ettl@fter\endcsname}
\makeatother %% <- revert @
定义\smuggleone
:
要将已定义的宏偷运到组末尾,使用 可能更有效\let
。\def
一个优点是它也适用于带有参数的宏:
{
\newcommand*\abc[1]{``#1''}%
\global\let\somethingunique\abc
\aftergroup\let\aftergroup\abc\aftergroup\somethingunique
}
\abc{This works!}
这使我们得出了 的一个可能的定义\smuggleone
。
\documentclass{article}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\begin{document}
\newcommand*\abc[1]{\textbf{#1}}%
{%
{%
\renewcommand*\abc[1]{``#1''}%
\smuggleone\abc
\abc{Local definition}
}\par
\abc{Local definition}
}\par
\abc{Global definition}
\end{document}
这里使用计数器的原因是,如果\somethingunique
每次走私时都使用,那么它实际上就不唯一了。每当连续发生多个走私操作时,由于您\smuggleone
多次使用来自同一组或来自另一个\smuggleone
使用组的组中的组,这将导致麻烦。因此,上述命令创建了\smuggle@<n>
第<n>
1 次使用。
通过尽可能多地重用这些命令序列,可以提高效率(内存方面),例如jfbu 的回答。
所有这些都适用于您的 MWE:
这是您的 MWE,有两处更改:(1)我添加了 的定义\smuggleone
和(2)我%\smuggleoutone#1
用替换了\smuggleone#2
。
\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\begin{document}
\begin{tikzpicture}[globalize/.code n args={2}{\xdef#2{#1}},
localize/.code n args={2}{\pgfmathsetmacro{#2}{#1}\typeout{#2}
\smuggleone#2
}]
\begin{scope}[local bounding box=extra]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
\pgfextra{\xdef\myangle{\n1}};
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (extra.north) {using \verb|\pgfextra|};
%
\begin{scope}[local bounding box=globalize,xshift=3cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[globalize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (globalize.north) {using \texttt{globalize}};
%
\xdef\myangle{7}
\begin{scope}[local bounding box=localize,xshift=6cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[localize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (localize.north) {attempt to smuggle};
%
\end{tikzpicture}
\end{document}
\node[anchor=south] at (globalize.north) {using \texttt{globalize}};
%
\xdef\myangle{7}
\begin{scope}[local bounding box=localize,xshift=6cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[localize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (localize.north) {attempt to smuggle};
%
\end{tikzpicture}
\end{document}
附录
这是一个\smuggle
可工作至深度 10 的宏。它不允许您跨 11 个边界走私任何东西,因为10
有两个令牌(是的,这是一个愚蠢的理由)。我可以让它适用于任何深度,但我喜欢当前定义的简短程度,而且任何理智的人似乎都不需要它。
语法是\smuggle[<depth>]{<macro>}
,默认值<depth>
是1
。它的工作原理是调用\smuggleone
然后还\aftergroup
ing \smuggle[<depth-1>]{<macro>}
。
\documentclass{article}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\DeclareRobustCommand\smuggle[2][1]{%
\smuggleone{#2}%
\ifnum#1>1
\aftergroup\smuggle\aftergroup[\expandafter\aftergroup\the\numexpr#1-1\aftergroup]\aftergroup#2%
\fi
}
\begin{document}
\newcommand*\abc[1]{\textbf{#1}}
{%
{%
{%
\renewcommand*\abc[1]{``#1''}%
\smuggle[2]{\abc}%
Definition at depth 3: \abc{Local definition}
}\par
Definition of depth 2: \abc{Local definition}
}\par
Definition of depth 1: \abc{Local definition}
}\par
Definition at depth 0: \abc{Global definition}
\end{document}
答案2
通常的做法是\expandafter
围绕组尾
\begingroup
% Various things
\def\result{some-tokens-that-need-to-escape}%
\expandafter\endgroup
\expandafter\def\expandafter\result\expandafter{\result}
如果使用expl3
\group_begin:
% Stuff to set
\tl_set:Nn \l_result_tl { some-tokens-that-need-to-escape }
\exp_args:NNNV \group_end:
\tl_set:Nn \l_result_tl \l_result_tl
无论哪种情况,都可以定义类似
\protected\def\smuggleone#1#2\endgroup{%
#2%
\expandafter\endgroup
\expandafter\def\expandafter#1\expandafter{#1}%
}
答案3
这涵盖了各种内容:
走私一个 (无参数;请参阅答案底部带有参数的宏) 宏到上一级,
(奇怪,为了好玩)把它偷运到上两层,但它在上一层仍然是未定义的,
(更有用)走私上一级内容某些宏,这是在组关闭任意多个标记后执行的方式(当然,如果它们仍然在该级别定义)。
一些全局定义的辅助宏的索引方式有些微妙(它们的索引从未全局增加),但我认为这没问题。我犹豫过是否要添加额外的代码让它们一旦使用就全局未定义,但最终没有这么做。相同的索引将被多次使用,但我认为没有不需要的东西会被覆盖。(尽管我可能需要再考虑一下)。
\documentclass{article}
\newcount\goodiescount
\makeatletter
\def\SmuggleMacro{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacro@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacro@aux #1#2{%
\expandafter\gdef\expandafter#1\expandafter
{\expandafter\def\expandafter#2\expandafter{#2}}%
\aftergroup#1%
}%
% This one will let the macro be known two levels higher,
% but not if only one level higher
\def\SmuggleMacroUpTwo{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacroUpTwo@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroUpTwo@aux#1#2{%
\expandafter\gdef\expandafter#1\expandafter
{\expandafter\def\expandafter#2\expandafter{#2}}%
\aftergroup\SmuggleValue
\aftergroup#1%
}%
\def\SmuggleValue{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleValue@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleValue@aux #1#2{%
\global\let#1#2%
\aftergroup#1%
}%
\makeatother
\begin{document}
\tt
\begingroup
\typeout{DEPTH 1}%
\def\fuzz{FUZZ defined at depth 1 and smuggled}%
\SmuggleMacro\fuzz
\begingroup
\typeout{DEPTH 2}%
\def\baz{BAZ defined at depth 2 and smuggled up two}%
\SmuggleMacroUpTwo\baz
\begingroup
\typeout{DEPTH 3}%
DEPTH 3\par
\def\foo{FOO defined at depth 3 and smuggled}%
\SmuggleMacro\foo
\def\bar{BAR defined at depth 3 and smuggled up two}%
\SmuggleMacroUpTwo\bar
END OF FIRST DEPTH 3\par
\endgroup
at depth 2 in-between the two depth 3\par
\string\foo\space has meaning \meaning\foo\space and will be smuggled again\par
\string\bar\space has meaning \meaning\bar\par
\SmuggleMacro\foo
\begingroup
DEPTH 3\par
\typeout{SECOND TIMES AT DEPTH 3}%
\def\foofoo{FOOFOO defined at (second) depth 3 and smuggled}%
\SmuggleMacro\foofoo
\def\Truc{\par Hello, I am \string\Truc\space
I was defined at depth 3, but got executed
at depth 2!\par
My own meaning is now: \meaning\Truc\par
\typeout{DEPTH 2 AFTER 3}}%
\show\Truc
\SmuggleValue\Truc
END OF SECOND DEPTH 3\par
\endgroup
BACK TO DEPTH 2 (after executing aftergroup tokens)\par
\show\Truc
\show\fuzz
\show\baz
\show\foo
\show\foofoo
\show\bar
\endgroup
BACK TO DEPTH 1 (after executing aftergroup tokens)\par
\string\foo\space has meaning \meaning\foo\par
\string\bar\space has meaning \meaning\bar\par
\typeout{DEPTH 1 AFTER 2}%
\show\fuzz
\show\baz
\show\foo
\show\foofoo
\show\bar
\endgroup
BACK TO DEPTH 0 (after executing aftergroup tokens)\par
\string\foo\space has meaning \meaning\foo\par
\typeout{DEPTH 0 AFTER 1}
\show\fuzz
\show\baz
\show\foo
\show\foofoo
\show\bar
\end{document}
DEPTH 1
DEPTH 2
DEPTH 3
SECOND TIMES AT DEPTH 3
> \Truc=macro:
->\par Hello, I am \string \Truc \space I was defined at depth 3, but got executed at depth 2!\par My own meaning is now: \meaning \Truc \par \typeout {DEPTH 2 AFTER 3}.
l.77 \show\Truc
DEPTH 2 AFTER 3
> \Truc=undefined.
l.82 \show\Truc
> \fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.83 \show\fuzz
> \baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.84 \show\baz
> \foo=macro:
->FOO defined at depth 3 and smuggled.
l.85 \show\foo
> \foofoo=macro:
->FOOFOO defined at (second) depth 3 and smuggled.
l.86 \show\foofoo
> \bar=macro:
->\mathaccent "7016\relax .
l.87 \show\bar
DEPTH 1 AFTER 2
> \fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.92 \show\fuzz
> \baz=undefined.
l.93 \show\baz
> \foo=macro:
->FOO defined at depth 3 and smuggled.
l.94 \show\foo
> \foofoo=undefined.
l.95 \show\foofoo
> \bar=macro:
->BAR defined at depth 3 and smuggled up two.
l.96 \show\bar
DEPTH 0 AFTER 1
> \fuzz=macro:
->FUZZ defined at depth 1 and smuggled.
l.101 \show\fuzz
> \baz=macro:
->BAZ defined at depth 2 and smuggled up two.
l.102 \show\baz
> \foo=undefined.
l.103 \show\foo
> \foofoo=undefined.
l.104 \show\foofoo
> \bar=macro:
->\mathaccent "7016\relax .
l.105 \show\bar
附录
我正在添加\SmuggleMacroNtimesUp <number>.\macro
将使\macro
已知<number>
级别上升(当然,在其含义使用这些级别已知的标记的范围内……)。目前只有无参数宏,因为这就是我开始的方式……
没有进行太多测试。事实上,只在下面的单个示例中进行了测试...
\documentclass{article}
\newcount\goodiescount
\makeatletter
\def\SmuggleMacro{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacro@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacro@aux #1#2{%
\expandafter\gdef\expandafter#1\expandafter
{\expandafter\def\expandafter#2\expandafter{#2}}%
\aftergroup#1%
}%
% This one will let the macro be known two levels higher,
% but not if only one level higher
\def\SmuggleMacroUpTwo{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacroUpTwo@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroUpTwo@aux#1#2{%
\expandafter\gdef\expandafter#1\expandafter
{\expandafter\def\expandafter#2\expandafter{#2}}%
\aftergroup\SmuggleValue
\aftergroup#1%
}%
\def\SmuggleValue{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleValue@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleValue@aux #1#2{%
\global\let#1#2%
\aftergroup#1%
}%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax \SmuggleMacroNtimesUp<number>.\macro
\def\SmuggleMacroNtimesUp{%
\advance\goodiescount 1 % not done globally!
\expandafter\SmuggleMacroNtimesUp@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroNtimesUp@aux#1#2.#3{%
\expandafter\gdef\expandafter#1\expandafter
{\expandafter\def\expandafter#3\expandafter{#3}}%
\aftergroup#1%
\expandafter\SmuggleMacroNtimesUp@a\the\numexpr#2-1.#1%
}%
\def\SmuggleMacroNtimesUp@a#1{%
\if0#1\expandafter\@gobbletwo
\else
\aftergroup\SmuggleValueNtimesUp
\aftergroup #1%
\expandafter\SmuggleNtimesUp@loop
\fi
}%
\def\SmuggleNtimesUp@loop#1{%
\aftergroup#1%
\if.#1\expandafter\aftergroup
\else
\expandafter\SmuggleNtimesUp@loop
\fi
}%
% This one makes **executes the macro**
% at all levels 1, 2, ..., N up.
% Syntax \SmuggleValueNtimesUp<number>.\macro
\def\SmuggleValueNtimesUp{%
\advance\goodiescount 1 % not done globally!
\expandafter\SmuggleValueNtimesUp@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleValueNtimesUp@aux#1#2.#3{%
\global\let#1#3%
\aftergroup#1%
\expandafter\SmuggleValueNtimesUp@a\the\numexpr#2-1.#1%
}%
\def\SmuggleValueNtimesUp@a#1{%
\if0#1\expandafter\@gobbletwo
\else
\aftergroup\SmuggleValueNtimesUp
\aftergroup #1%
\expandafter\SmuggleNtimesUp@loop
\fi
}%
\makeatother
\begin{document}
\ttfamily
\def\foo{}
{{{{{{{{% 8 deep
{{{{{{{{% 16 deep
\def\foo{FOO defined at 16 will be made known all the way to 3}%
\SmuggleMacroNtimesUp13.\foo
16.\foo\par}%
15.\foo\par}%+1
14.\foo\par}%+2
13.\foo\par}%+3
12.\foo\par}%+4
11.\foo\par}%
10.\foo\par}%
9.\foo\par}%
8.\foo\par}%+8
7.\foo\par}%
6.\foo\par}%
5.\foo\par}%
4.\foo\par}%+12
3.\foo\par}%+13
2.\foo\par}%
1.\foo\par}%
0.\foo\par
\end{document}
最终版本
在@Circumscribe 示例的压力下,我已重构以处理带参数的宏。没有进行太多测试...添加了关于将含义移至顶层(又称底层)的@marmot 查询...
因此定义的事物是
\SmuggleMacro \foo
:使其\foo
含义上升一个层次,\SmuggleMacroUpTwo \foo
:使\foo
恢复其含义上升两级(但不是上升一级……)\SmuggleMacroNtimesUp <number>.\foo
: 使嵌套层数越少\foo
,其含义就越清晰。必须至少<number>
与 一起使用。<number>
1
\SmuggleValueNtimesUp <number>.\foo
:一旦剩下较多嵌套的级别,就通过 so 执行嵌套级别较少的级别\foo
的含义。其本身(如果不是全局定义的)不会被走私。<number>
\aftergroup
\foo
\SmuggleMacroToTop\foo
:\foo
在底层(原文如此)让人知道,但在任何中间层都不为人所知(当然,从内到外,一旦我们到达底层,下次我们进入一个组时\foo
就会被知道)。
(如果代码看起来很疯狂,那也是因为它试图定义较少的辅助存储宏,通过保持这种永远不会全局踩踏这些存储对象的索引的想法)
\documentclass{article}
\usepackage{geometry}
\newcount\goodiescount
\makeatletter
\def\SmuggleMacro{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacro@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacro@aux#1#2{%
\global\let#1#2%
\aftergroup\let
\aftergroup#2%
\aftergroup#1%
}%
% This one will let the macro be known two levels higher,
% but not if only one level higher
\def\SmuggleMacroUpTwo{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleMacroUpTwo@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroUpTwo@aux#1#2{%
\global\let#1#2%
\aftergroup\SmuggleLet
\aftergroup#2%
\aftergroup#1%
}%
\def\SmuggleLet{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleLet@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleLet@aux#1#2#3{%
\global\let#1#3%
\aftergroup\let
\aftergroup#2%
\aftergroup#1%
}%
%
% This one makes known the macros 1, 2, ..., N levels up.
% Syntax \SmuggleMacroNtimesUp<number>.\macro
\def\SmuggleMacroNtimesUp{%
\advance\goodiescount 1 % not done globally!
\expandafter\SmuggleMacroNtimesUp@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroNtimesUp@aux#1#2.#3{%
\global\let#1#3%
\aftergroup\let
\aftergroup#3%
\aftergroup#1%
\expandafter\SmuggleMacroNtimesUp@a\the\numexpr#2-1.#3%
}%
%\long\def\@gobblethree#1#2#3{}%
\def\SmuggleMacroNtimesUp@a#1{%
\if0#1\expandafter\@gobbletwo
\else
\aftergroup\SmuggleMacroNtimesUp
\aftergroup #1%
\expandafter\SmuggleNtimesUp@loop
\fi
}%
\def\SmuggleNtimesUp@loop#1{%
\aftergroup#1%
\if.#1\expandafter\aftergroup
\else
\expandafter\SmuggleNtimesUp@loop
\fi
}%
\def\SmuggleValueNtimesUp{%
\advance\goodiescount 1 % not done globally!
\expandafter\SmuggleValueNtimesUp@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleValueNtimesUp@aux#1#2.#3{%
\global\let#1#3%
\aftergroup#1%
\expandafter\SmuggleValueNtimesUp@a\the\numexpr#2-1.#1%
}%
\def\SmuggleValueNtimesUp@a#1{%
\if0#1\expandafter\@gobbletwo
\else
\aftergroup\SmuggleValueNtimesUp
\aftergroup #1%
\expandafter\SmuggleNtimesUp@loop
\fi
}%
% \SmuggleMacroToTop
\def\SmuggleMacroToTop{%
\ifnum\currentgrouplevel=\z@
\expandafter\@gobble
\else
\expandafter\SmuggleMacro@ToTop
\fi
}%
\def\SmuggleMacro@ToTop{%
\advance\goodiescount 1 % not done globally!
\expandafter\SmuggleMacroToTop@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleMacroToTop@aux#1#2{%
\global\let#1#2%
\aftergroup\SmuggleLetToTop
\aftergroup#2%
\aftergroup#1%
}%
\def\SmuggleLetToTop{%
\ifnum\currentgrouplevel=\z@
\expandafter\let
\else
\expandafter\SmuggleLet@ToTop
\fi
}%
\def\SmuggleLet@ToTop{%
\advance\goodiescount 1 % not done globally !
\expandafter\SmuggleLetToTop@aux
\csname Goodies\the\goodiescount\endcsname
}%
\def\SmuggleLetToTop@aux#1#2#3{%
\global\let#1#3%
\aftergroup\SmuggleLetToTop
\aftergroup#2%
\aftergroup#1%
}%
\makeatother
\begin{document}
\ttfamily
\def\foo{}
{{{{{{{{% 8 deep
{{{{{{{{% 16 deep
\def\BAR#1#2#3{Hello, I am BAR}%
\SmuggleMacro\BAR
\SmuggleMacroToTop\BAR
\def\BAZ#1#2#3#4{Hello, I am BAZ}%
\SmuggleMacroUpTwo\BAZ
\def\foo#1#2{FOO defined at 16 will be made known all the way to 3}%
\SmuggleMacroNtimesUp13.\foo
16.FOO \meaning\foo\par
16.BAZ \meaning\BAZ\par
16.BAR \meaning\BAR\par
\def\x{\leavevmode\llap{aaa }}%
\SmuggleValueNtimesUp7.\x
\medskip}%
15.FOO \meaning\foo\par
15.BAZ \meaning\BAZ\par
15.BAR \meaning\BAR\par\medskip}%
14.FOO \meaning\foo\par
14.BAZ \meaning\BAZ\par
14.BAR \meaning\BAR\par\medskip}%
13.FOO \meaning\foo\par}%+3
12.FOO \meaning\foo\par}%+4
11.FOO \meaning\foo\par}%
10.FOO \meaning\foo\par}%
9.FOO \meaning\foo\par
9.BAR \meaning\BAR\par
}%
8.FOO \meaning\foo\par}%+8
7.FOO \meaning\foo\par}%
6.FOO \meaning\foo\par}%
5.FOO \meaning\foo\par
5.BAR \meaning\BAR\par}%
4.FOO \meaning\foo\par}%+12
3.FOO \meaning\foo\par}%+13
2.FOO \meaning\foo\par}%
1.FOO \meaning\foo\par}%
0.FOO \meaning\foo\par
0.BAR \meaning\BAR\par
\end{document}
答案4
PGF 3.1.3 中的新功能
此版本引入了ConTeXt 中的\pgfutil@pushmacro
和\pgfutil@popmacro
。使用这两个操作,您可以将宏推送到堆栈并从堆栈中弹出。底层宏定义也使用全局变量(唯一可能绕过的方法可能是使用 Lua),但宏名称足够模糊,因此不太可能发生冲突。
它与其他答案并没有太大区别,但这种方法现在已内置于 PGF 中。
\documentclass{article}
\usepackage{pgf}
\begin{document}
\makeatletter
\def\zz{a}
\show\zz
{
{
\def\zz{b}
\show\zz
\pgfutil@pushmacro\zz
\def\zz{c}
\show\zz
}
\pgfutil@popmacro\zz
\show\zz
}
\show\zz
\makeatother
\end{document}
> \zz=macro:
->a.
l.8 \show\zz
?
> \zz=macro:
->b.
l.13 \show\zz
?
> \zz=macro:
->c.
l.16 \show\zz
?
> \zz=macro:
->b.
l.19 \show\zz
?
> \zz=macro:
->a.
l.21 \show\zz
?