[Tex/LaTex] How to expand a macro used in the range of a foreach loop

expansionforeachtikz-pgftikz-trees

Motivating Example:

Suppose that I want to draw the following tree:

    root
  /           / \     /     A   B   C   D

This is easy enough to do by explicitly drawing the two subtrees
as children of the root node and using a child foreach in
each subtree to draw the leaves. For instance:

\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}

enter image description here

However, even in this simple example, a fair amount of code is
duplicated across the two subtrees. If the leaves were more
complex, code duplication would be an even bigger disadvantage.

An alternate idea for drawing this tree attempts to use nested
child foreach constructs to reduce the code duplication:

\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}

Unfortunately, this code produces the wrong tree structure:

enter image description here

Interestingly, Martin Scharrer's answer to TikZ \foreach loop with macro-defined list suggests that the code should work as is. The only difference that I can see is that that question uses \foreach rather than child foreach.

Problem:

The underlying problem seems to be that \xs is not
expanded before the inner loop is executed. As a result,
TikZ is treating the inner loop as having a range that is a
singleton set, namely \xs (unexpanded). If \xs could be
expanded before the inner loop is executed, then TikZ should
treat the inner loop as having a range that is {A,B} in one
case and {C,D} in the other case.

Question:

How can I expand a macro, such as \xs above, used inside
the range of a foreach loop?

Best Answer

Deep in the bowels of TikZ, the child creation code calls \foreach to create the children. However, by the time it has done so then the various parameters to the \foreach loop have passed through several macros. Each time, there is the potentiality to strip off an outer pair of braces. Rather than try to keep track of this, TikZ gets round it by ensuring that the inner call to \foreach is of the form \foreach#2in{#3} which means that in your situation it sees \foreach \x in {\xs}. This is what is preventing the \foreach from expanding the macro to find the list: if it saw \foreach \x in \xs there would be no problem.

So we need to hack the code. When the foreach is first noticed we need to peek ahead to see if the range is surrounded by braces or not and remember that for when we get to the bottom. There are probably far more elegant ways of doing this than the following which involves a reasonable amount of repeated code (I copied the places where TikZ has the \foreach and removed the braces and then put in a decision as to which set of macros to use at the outset).

\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}

Produces:

TikZ foreach tree children code