关于\aftergroup:

关于\aftergroup:

我正在寻找一种可以让我在组外“广播”宏的方法。具体示例包括 中的路径和范围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

代码产生错误。

问题:是否可以将宏偷运到组外而不使其成为全局的?

“奖金”:当然,如果能解释一下所有走私命令的作用就太好了。

“奖金”: 可以想象这些方法可能独立于 TiZ,所以如果有办法不让它们依赖 TiZ 正在加载,这会很棒,但肯定不是必需的。

答案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 指出这一点,以下是几个示例:1234

即使你不应该使用该包,\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然后还\aftergrouping \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

? 

相关内容