我将列出两份 LaTeX 手稿。第一份是手册中给出的用于演示密钥有用性的示例/tikz/graph/parse
,它编译成功,而第二份编译失败。在列出第一份手稿并展示其输出后,我将论证第二份手稿与第一份手稿等同。显然这个论点是错误的,但我不知道为什么。
考虑以下 LaTeX 手稿,它本质上是 TikZ & PGF 手册 3.0.1a 版(第 265 页)第 19 章(“指定图形”)第 19.3 节(“图形路径命令的语法”)第 19.3.2 小节(“组规范的语法”)末尾的示例的副本。
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{graphs}
\begin{document}
\def\mychain#1{
\def \mytext{1}
\foreach \i in {2,...,#1} {
\xdef\mytext{\mytext -> \i}
}
}
\tikzgraphsset{my chain/.style={
/utils/exec=\mychain{#1},
parse/.expand once=\mytext}
}
\tikz \graph { [my chain=4] };
\end{document}
最终渲染的图片为(不按比例)
我将尽我所理解来解释输出。
- 该
\tikzgraphsset
命令只是使用路径前缀执行其键/tikz/graphs
(该\tikzgraphsset
命令在 p. 262 中描述)。因此,键/tikz/graphs/my chain
成为扩展为 的缩写/utils/exec=\mychain{#1}, parse/.expand once=\mytext
。 - 接下来
\graph ...
执行命令。唯一要做的就是处理组选项([my chain=4]
)。组选项使用路径前缀执行/tikz/graphs
(第 264 页中对此进行了描述)。因此/tikz/graphs/mychain=4
执行了键分配。这扩展为/utils/exec=\mychain{4},/tikz/graphs/parse/.expand once=\mytext
。 - 现在执行分配
/utils/exec=\mychain{4}
。此键仅执行分配的值(此键在“密钥管理”一章第 893 页中描述),因此宏\mychain{4}
得到扩展。 - 最后一步
/tikz/graphs/parse/.expand once=\mytext
是执行赋值。密钥处理程序.expand once
导致\mytext
将 的替换文本(即 )1 -> 2 -> 3 -> 4
赋值给/tikz/graphs/parse
(.expand once
密钥处理程序在“密钥管理”一章第 890 页中描述)。赋值给 密钥的效果/tikz/graphs/parse
是将赋值的值插入到当前组的开头,就像您在那里输入它一样(/tikz/graphs/parse
在第 265 页中描述)。
总之,上述手稿应等同于以下内容:
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{graphs}
\begin{document}
\def \mychain#1{
\def \mytext{1}
\foreach \i in {2,...,#1} {
\xdef\mytext{\mytext -> \i}
}
}
\tikz \graph { \mychain{4} 1->2->3->4 };
\end{document}
但最后的手稿编译失败,并pdftex
报告以下错误消息:
ERROR: Undefined control sequence.
--- TeX said ---
\mychain #1-> \def \mytext
{1} \foreach \i in {2,...,#1} { \xdef \mytext {\m...l.11 \tikz \graph { \mychain{4} 1->2->3->4 }
如果有人能解释第二个例子为什么会失败,而且更重要的是,能解释第一个例子是如何工作的,我将不胜感激。我希望能够预测类似的例子会如何发挥作用。
答案1
以下是 TikZ“引擎”如何处理路径的粗略概述*
\graph { [my chain=4] };
假设调用了图解析器\graph@parser
(实际上并没有调用,这只是为了说明)。然后上述语句扩展为
{\graph@parser [my chain=4] };}
换句话说,这两个标记\graph {
被控制序列替换\graph@parser
,然后整个语句被 TeX 组包围。
现在 TeX 打开了一个新的范围,并继续处理以下输入流:
\graph@parser [my chain=4] };}
当解析器看到 时[
,它会将整个[...]
构造替换为
\pgfkeys{/tikz/graphs/.cd,...}
,并在执行选项后恢复解析。换句话说,最后一个代码片段扩展为
\pgfkeys{/tikz/graphs/.cd,my chain=4}\graph@parser };}
键/tikz/graphs/.cd
表示具有相对路径(或使用手册术语的“部分路径”)的键将相对于基本路径进行解释/tikz/graphs
。因此,最后一个代码片段扩展为
\pgfkeys{/tikz/graphs/my chain=4}\graph@parser };}
由于/tikz/graphs/my chain
是一种样式,因此在用指定值 4 替换参数 #1 后,它将被其“替换文本”替换,因此最后一个代码片段扩展为
\pgfkeys{/utils/exec=\mychain{4},/tikz/graphs/parse/.expand once=\mytext}%
\graph@parser };}
正如原帖所述,这扩展为
\mychain{4}\pgfkeys{/tikz/graphs/parse/.expand once=\mytext}\graph@parser };}
\mychain[4}
最终扩展为\gdef\mytext{1->2->3->4}
。因此,宏\mytext
保存在 TeX 的内部状态中,最后一个代码片段简化为
\pgfkeys{/tikz/graphs/parse/.expand once=\mytext}\graph@parser };}
正如原帖所述,这扩展为
\pgfkeys{/tikz/graphs/parse=1->2->3->4}\graph@parser };}
键的操作/tikz/graphs/parse
是将其参数插入到控制序列之后\graph@parser
。因此,最后一个代码片段扩展为
\graph@parser 1->2->3->4};}
所有这些扩展的总体效果就像原始路径(\graph { [my chain=4] };
)被替换为
{%
\gdef\mytext{1->2->3->4}%
\graph{1->2->3->4}%
}
这反过来相当于
\def\mytext{1->2->3->4}%
\graph{1->2->3->4}
* 需要明确的是:这意味着我没有讲述完整的故事,并且我只是说了一些善意的谎言,以使呈现更清晰、更干净。