激励示例:
假设我想绘制以下树:
root
/ / \ / A B C D
这很容易做到, 只需明确地将两个子树绘制为child
根的ren node
,并使用child foreach
每个子树来绘制叶子。例如:
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\node {root} [level/.append style={sibling distance=2cm/#1}]
child {
coordinate
child foreach \x in {A,B} {
node {\x}}}
child {
coordinate
child foreach \x in {C,D} {
node {\x}}};
\end{tikzpicture}
\end{document}
然而,即使在这个简单的例子中,两棵子树上也有相当多的代码是重复的。如果叶子更复杂,代码重复将是一个更大的缺点。
绘制此树的另一种想法是尝试使用嵌套
child foreach
构造来减少代码重复:
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\node at (5cm,0) {root} [level/.append style={sibling distance=2cm/#1}]
child foreach \xs in {{A,B}, {C,D}} {
coordinate
child foreach \x in \xs {
node {\x}}};
\end{tikzpicture}
\end{document}
不幸的是,此代码产生了错误的树结构:
有趣的是,Martin Scharrer 的回答到TikZ \foreach 循环与宏定义列表表明代码应该按原样工作。我能看到的唯一区别是该问题使用\foreach
而不是child foreach
。
问题:
潜在的问题似乎是\xs
在执行内循环之前未展开。因此,TikZ 将内循环视为具有单例集的范围,即\xs
(未展开)。如果可以在执行内循环之前展开,那么 TikZ 应该将内循环视为具有一种情况和另一种情况的\xs
范围。{A,B}
{C,D}
问题:
我如何扩展\xs
循环范围内使用的宏(例如上面的宏)foreach
?
答案1
在 TikZ 的深处,子代创建代码调用\foreach
来创建子代。然而,当它完成创建子代时\foreach
,循环中的各种参数已经通过了几个宏。每次都有可能剥去一对外括号。TikZ 不会试图跟踪这一点,而是通过确保内部调用的\foreach
形式来绕过它\foreach#2in{#3}
,这意味着在您的情况下它会看到\foreach \x in {\xs}
。这就是阻止\foreach
扩展宏来查找列表的原因:如果它看到,\foreach \x in \xs
就不会有问题。
所以我们需要破解代码。当foreach
第一次注意到时,我们需要提前查看范围是否被括号包围,并在到达底部时记住这一点。可能有比以下更优雅的方法来实现这一点,这涉及大量重复的代码(我复制了 TikZ 有 的地方\foreach
并删除了括号,然后在一开始就决定使用哪组宏)。
\documentclass{article}
%\url{http://tex.stackexchange.com/q/67187/86}
\usepackage{tikz}
\makeatletter
\def\tikz@collect@children@foreach[#1]foreach#2in{%
\pgfutil@ifnextchar\bgroup{\tikz@collect@children@foreachbr{#1}{#2}}%
{\tikz@collect@children@foreachnbr{#1}{#2}}}
\def\tikz@collect@children@foreachbr#1#2#3{%
\pgfutil@ifnextchar\bgroup{\tikz@collect@children@foreachA{#1}{#2}{#3}}{\tikz@collect@children@foreachA{#1}{#2}{#3}{}}}
\def\tikz@collect@children@foreachnbr#1#2#3{%
\pgfutil@ifnextchar\bgroup{\tikz@collect@children@foreachAnbr{#1}{#2}{#3}}{\tikz@collect@children@foreachAnbr{#1}{#2}{#3}{}}}
\def\tikz@collect@children@foreachAnbr#1#2#3#4{%
\expandafter\def\expandafter\tikz@children@list\expandafter
{\tikz@children@list\tikz@childrennodesnbr[#1]{#2}{#3}{#4}}%
\c@pgf@counta=\tikznumberofchildren%
\foreach#2in#3%
{%
\global\advance\c@pgf@counta by1\relax%
}%
\tikznumberofchildren=\c@pgf@counta%
\tikz@collect@children%
}
\def\tikz@childrennodesnbr[#1]#2#3#4{%
\c@pgf@counta=\tikznumberofcurrentchild\relax%
\setbox\tikz@tempbox=\box\tikz@figbox%
\foreach#2in#3{%
\tikznumberofcurrentchild=\c@pgf@counta\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
\tikz@childnode[#1]{#4}%
% we must now make the current child number and the figbox survive
% the group
\global\c@pgf@counta=\tikznumberofcurrentchild\relax%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
}%
\tikznumberofcurrentchild=\c@pgf@counta\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node at (5cm,0) {root} [level/.append style={sibling distance=2cm/#1}]
child foreach \xs in {{A,B}, {C,D}} {
coordinate
child foreach \x in \xs {
node {\x}}};
\end{tikzpicture}
\end{document}
生成: