PGF 中 \pgf@marshal 的惯用用法

PGF 中 \pgf@marshal 的惯用用法

\pgf@marshal我在 PGF 包中遇到了一种明显的惯用用法,我不知道为什么要使用它。

例如来自pgflibraryshapes.gates.logic.US.code.tex

{%
    \edef\pgf@marshal{%
        \noexpand\pgfpatharc{90}{-90}{\the\pgf@yc}%
    }%
    \pgf@marshal%
}

这可能是一个简单的问题,但为什么不直接

\pgfpatharc{90}{-90}{\the\pgf@yc}

答案1

虽然\pgfpatharc不使用\pgf@yc(至少在它已经被评估并存储在其他地方之前),而且这样做是安全的

\pgfpatharc{90}{-90}{\pgf@yc}

(没有\the偶数),手册本身警告

注意力:PGF 使用这些寄存器来执行路径操作。出于效率原因,路径命令并不总是保护它们。因此,代码

\pgfpointadd{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}

可能会失败:在 中\pgfpointadd\pgf@xa和 友元寄存器可能会被修改。具体来说, 可能会在 求值\pgf@xb之前发生改变\pgfpoint{\pgf@xb}{\pgf@yb}。正确的做法是先使用 扩展所有内容\edef,然后再处理值,这会导致不必要的昂贵操作。当然,只需查看 的源代码以\pgfpointadd查看使用了哪些寄存器,就可以避免这种情况。

(有趣的是,给出的例子实际上可以工作,但不是

\pgfpointadd{\pgfpoint{\pgf@xb}{\pgf@yb}}{\pgfpoint{\pgf@xa}{\pgf@ya}}

因为第一个点的坐标将存储在第二个点的坐标中\pgf@xa\pgf@ya稍后将添加到第二个点的坐标中。)

在 的情况下\pgfpatharc,在使用#3之前进行评估,并且这仅在范围内发生,因此在 之后仍然具有相同的值。\pgf@yc\pgf@yc\pgfpatharc

所以,是的,电路库的开发人员可以查看一下其定义\pgfpatharc并确定它没有意义。

但还有其他几点需要考虑:

  • \pgfpatharc在编写电路库时,该命令可能工作得有所不同。
  • \pgfpatharc命令将来可能会改变。
  • 电路库的作者在其他地方注意到了冲突并改变了所有类似的宏调用,这样他们就再也不必处理这样的问题了。

答案2

对于那些不太熟悉 TeX 执行模型的人来说,这是另一个视角。


维基词典:

marshal(动词)
4. 收集数据以供传输。5
. (计算,传递)将对象序列化为由字节序列表示的编组状态,该字节序列稍后可以转换回具有等效属性的对象。

虽然在计算上下文中通常需要含义 5,但在这种情况下含义 4 似乎更相关。但它也可以理解为“将代码序列化为标记列表,然后将标记列表作为代码执行”——但在 TeX 中,代码和标记列表基本上是同一件事。

总结:

  • 默认情况下,TeX 命令是“从外到内”执行的 - 也就是说,与典型的程序语言不同,在“传递给函数”之前“评估”函数参数,在 TeX 中,参数是逐字传递给宏的。

    • 在 Python 中,f(1+2)f(3)是相同的,因为在接收它之前1+2被评估为。在 TeX 中,和并不相同。3f\f{\numexpr 1+2\relax}\f{3}
  • 在这种情况下,正如另一个答案,调用者希望在将数字传递给函数之前尽早对其进行评估。

  • TeX 操纵 token 的机制非常有限。你看到的代码相当于

    eval(r"\pgfpatharc{90}{-90}{" + pgfyc + "}")
    

    在 Python 语法中---代码作为标记列表进行操作然后执行。

    另一种方法是在代码的任何地方添加\expandafter,但这肯定效率较低。(还有\expanded{\unexpanded{...}},但是在编写代码时并不存在。)

  • 这个习语的其他地方也用到过,例如如何pgfplots在循环中使用情节\edef\temp{...}\temp

  • 前缀pgf@表示宏名称是 pgf 包内部的。如果您不编辑 PGF/TikZ 源代码,则应创建自己的名称,而不要重复使用\pgf@marshal

相关内容