激励示例:

激励示例:

激励示例:

假设我想绘制以下树:

    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}

生成:

TikZ foreach 树子代码

相关内容