\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 中,和并不相同。3
f
\f{\numexpr 1+2\relax}
\f{3}
- 在 Python 中,
在这种情况下,正如另一个答案,调用者希望在将数字传递给函数之前尽早对其进行评估。
TeX 操纵 token 的机制非常有限。你看到的代码相当于
eval(r"\pgfpatharc{90}{-90}{" + pgfyc + "}")
在 Python 语法中---代码作为标记列表进行操作然后执行。
另一种方法是在代码的任何地方添加
\expandafter
,但这肯定效率较低。(还有\expanded{\unexpanded{...}}
,但是在编写代码时并不存在。)这个习语的其他地方也用到过,例如如何
pgfplots
在循环中使用情节和\edef\temp{...}\temp
。前缀
pgf@
表示宏名称是 pgf 包内部的。如果您不编辑 PGF/TikZ 源代码,则应创建自己的名称,而不要重复使用\pgf@marshal
。