TransWikia.com

TikZ - foreach, expansion problem, optional argument and node

TeX - LaTeX Asked on September 20, 2020

I am trying to use foreach to simplify the 1st env. code in my MN(ot)WE below.

There is an expansion problem regarding the optional argument in the 2nd code. Here is the output.

enter image description here

How can I fix this ?

documentclass[12pt]{article}

RequirePackage{forest}
usetikzlibrary{calc}
useforestlibrary{linguistics}

newcommandptreeComment[3][black]{
    node [anchor=mid west] at (#2.mid -| ptreecomment coord) {%
        textcolor{#1}{#3}%
    };
}

begin{document}

begin{forest}
    for tree = {%
        sn edges,
        grow'  = 0,
        l      = 2.5cm,
        s sep  = 1.2cm,
        anchor = parent,
    },
    tikz+={
        coordinate (ptreecomment coord) at (current bounding box.east);
    },
    [
        [$A$
            [$B$, name = nB]
            [$C$, name = nC]
        ]
        [$D$
            [$E$, name = nE]
            [$F$, name = nF]
        ]
    ]
    %
     ptreeComment{nB}{$X = 1$}
     ptreeComment[red]{nC}{$X = 3$}
     ptreeComment[orange]{nE}{$X = 3$}
     ptreeComment[black!60!green]{nF}{$X = 5$}
end{forest}

begin{forest}
    for tree = {%
        sn edges,
        grow'  = 0,
        l      = 2.5cm,
        s sep  = 1.2cm,
        anchor = parent,
    },
    tikz+={
        coordinate (ptreecomment coord) at (current bounding box.east);
    },
    [
        [$A$
            [$B$, name = nB]
            [$C$, name = nC]
        ]
        [$D$
            [$E$, name = nE]
            [$F$, name = nF]
        ]
    ]
    % 
    foreach color/name/text in {black/nB/$X = 1$,
                                    red/nC/$X = 3$,
                                    orange/nE/$X = 3$,
                                    black!60!green/nF/$X = 5$}
    {
        ptreeComment[color]{name}{text}
    }
end{forest}

end{document}

On the other hand, the following code works without problem.

documentclass[12pt]{article}

RequirePackage{tikz}

newcommandtest[2][black]{
    Option: #1 et Arg: #2par
}

newcommandcolorize[2][black]{
    textcolor{#1}{#2}par
}

begin{document}

foreach opt/arg in {black/nB,red/nC,orange/nE,green/nF}{
    test[opt]{arg}
}

bigskip

foreach opt/arg in {black/nB,red/nC,orange/nE,green/nF}{
    colorize[opt]{arg}
}

end{document}

3 Answers

The problem comes indeed from the use of the name color. The use of col makes all things work... :-)

Answered by projetmbc on September 20, 2020

I don't know why you define a function to change nodes when all you have to do is create a style.

Here, I created the my note style which has 3 arguments : color, name, and text.

my note/.style n args={3}{text=#1,name=#2,label={[#1]right:#3}},
my note/.default={black}{}{},

screenshot

documentclass[12pt]{article}

RequirePackage{forest}
usetikzlibrary{calc}
useforestlibrary{linguistics}

%newcommandptreeComment[3][black]{
%    node [anchor=mid west] at (#2.mid -| ptreecomment coord) {%
%        textcolor{#1}{#3}%
%    };
%}

begin{document}

begin{forest}
my note/.style n args={3}{text=#1,name=#2,label={[#1]right:#3}},
my note/.default={black}{}{},
    for tree = {%
        sn edges,
        grow'  = 0,
        l      = 2.5cm,
        s sep  = 1.2cm,
        anchor = parent,
    },
    tikz+={
        coordinate (ptreecomment coord) at (current bounding box.east);
    },
    [
        [$A$
            [$B$,my note]
            [$C$,my note ={red}{nC}{$X=3$}]
        ]
        [$D$
            [$E$,my note ={blue}{nE}{$X=3$}]
            [$F$,my note ={black!60!green}{nF}{$X=5$}]
        ]
    ]
    %
%     ptreeComment{nB}{$X = 1$}
%     ptreeComment[red]{nC}{$X = 3$}
%     ptreeComment[orange]{nE}{$X = 3$}
%     ptreeComment[black!60!green]{nF}{$X = 5$}
end{forest}

end{document}

Answered by AndréC on September 20, 2020

You should be more careful when choosing names for local variables in foreach:

% xcolor.sty, line 756:
deftextcolor#1#{@textcolor{#1}}

% xcolor.sty, line 757:
def@textcolor#1#2#3{protectleavevmode{color#1{#2}#3}}

You're using textcolor, which internally uses color: if you redefine color you can't expect that `textcolor works; actually you clearly see why the color name is printed.

There is a different way to cope with the problem, without using local variables.

documentclass[12pt]{article}

usepackage{forest}
usetikzlibrary{calc}
useforestlibrary{linguistics}

usepackage{xparse}
ExplSyntaxOn

NewDocumentCommand{lforeach}{ s O{} m +m }
 {
  IfBooleanTF{#1}
   {
    manual_lforeach:non { #2 } { #3 } { #4 }
   }
   {
    manual_lforeach:nnn { #2 } { #3 } { #4 }
   }
 }

int_new:N g__manual_foreach_map_int

cs_new_protected:Nn manual_lforeach:nnn
 {
  keys_set:nn { manual/lforeach } { single }
  keys_set:nn { manual/lforeach } { #1 }
  clist_set:Nn l__manual_lforeach_list_clist { #2 }
  int_gincr:N g__manual_foreach_map_int
  __manual_lforeach_define:n { #3 }
  clist_map_inline:Nn l__manual_lforeach_list_clist
   {
    use:c { __manual_lforeach_ int_use:N g__manual_foreach_map_int _action:w } ##1 q_stop
   }
  int_gdecr:N g__manual_foreach_map_int
 }
cs_generate_variant:Nn manual_lforeach:nnn { no }

cs_new_protected:Nn __manual_lforeach_define:n
 {
  exp_last_unbraced:NcV
   cs_set:Npn
   { __manual_lforeach_ int_use:N g__manual_foreach_map_int _action:w }
   l__manual_lforeach_format_tl
   q_stop
   {#1}
 }

keys_define:nn { manual/lforeach }
 {
  format .tl_set:N = l__manual_lforeach_format_tl,
  single .code:n = tl_set:Nn l__manual_lforeach_format_tl { ##1 },
  double .code:n = tl_set:Nn l__manual_lforeach_format_tl { ##1/##2 },
  triple .code:n = tl_set:Nn l__manual_lforeach_format_tl { ##1/##2/##3 },
 }
ExplSyntaxOff

newcommandptreeComment[3][black]{%
    node [anchor=mid west] at (#2.mid -| ptreecomment coord) {%
        textcolor{#1}{#3}%
    };
}

begin{document}

begin{forest}
    for tree = {%
        sn edges,
        grow'  = 0,
        l      = 2.5cm,
        s sep  = 1.2cm,
        anchor = parent,
    },
    tikz+={
        coordinate (ptreecomment coord) at (current bounding box.east);
    },
    [
        [$A$
            [$B$, name = nB]
            [$C$, name = nC]
        ]
        [$D$
            [$E$, name = nE]
            [$F$, name = nF]
        ]
    ]
    % 
    lforeach[triple]% triple means #1/#2/#3
     {
      black/nB/$X = 1$,
      red/nC/$X = 3$,
      orange/nE/$X = 3$,
      black!60!green/nF/$X = 5$
     }
     {ptreeComment[#1]{#2}{#3}}
end{forest}

end{document}

The macro lforeach has an optional argument for stating options; the possible ones are

  • single (default), when you only want to cycle over a standard comma separated list;
  • double if the list is of the form 1/a,2/b,... and the items before and after the / are referred to with #1 and #2;
  • triple if the list is of the form `1/a/i,2/b/ii,... (used in the example);
  • format to choose whatever delimiters you wish, with any number of arguments (up to nine, of course); for instance, triple is the same as format=#1/#2/#3.

The *-variant expands once the first mandatory argument (if the list is stored in a macro). The first mandatory argument has the list, the second one has the code to be executed, where instead of “variables” you use #1, #2 and so on.

For instance, you get the same output with

    lforeach[format=C(#1)N(#2)T(#3)]
     {
      C(black)N(nB)T($X = 1$),
      C(red)N(nC)T($X = 3$),
      C(orange)N(nE)T($X = 3$),
      C(black!60!green)N(nF)T($X = 5$)
     }
     {ptreeComment[#1]{#2}{#3}}

(this is just for showing the possibilities).

enter image description here

Answered by egreg on September 20, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP