TransWikia.com

Tikz /foreach problem

TeX - LaTeX Asked by David Marchant on March 23, 2021

The code below used to draw a plot of a permutation:

documentclass[a4paper]{article}
usepackage{tikz}

newcommand{normaldot}[1]{ % Make a dot of fixed absolute size.
    node at #1 {normalsize $bullet$};
}

newcommand{plotperm}[1]{% plot a permutation
    foreach j [count=i] in {#1} {
        normaldot{(i,j)}{};
    };
}
newcommand{plotpermgrid}[1]{% plot a permutation in a grid
    foreach i [count=n] in {#1} {};  % Now n stores the number of entries of the permutation. 
    foreach i in {0,1,2,...,n}{
        draw [color=darkgray] ({i+0.5}, 0.5)--({i+0.5}, {n+0.5});
        draw [color=darkgray] (0.5, {i+0.5})--({n+0.5}, {i+0.5});
    }
    plotperm{#1};
}

begin{document}
    begin{tikzpicture}
        plotpermgrid{2,4,1,3}
    end{tikzpicture}
end{document}  

Using TexLive2017, this would result in a grid showing the points of the permutation (2,4,1,3) like thisplot of permutation 2413

With TexLive2019, I get error messages, starting with "Undefined control sequence. plotpermgrid{2,4,1,3}"

The problem appears to be related to the line

foreach i [count=n] in {#1} {};

In TexLive2017, this sets n to the number of elements of the permutation, and, critically, allows n to be used after the foreach has finished. In TexLive2019, TexLine2020, and MikTex, n is set within the loop, but cannot be used afterwards.

I could fix this by adding a second parameter for the number of elements, but this doesn’t seem to be an elegant solution, and would be susceptible to errors.

I’ve tried using counters, but (with a counter called gridsize), I get errors when I write foreach i in {0,1,2,...,{thegridsize}}.

I don’t have the option to go back to TexLive2017, and I have a large number of LaTeX documents which use plotpermgrid as defined above.

How can I change the definition of plotpermgrid so that it works using TexLive2019 or later, while still taking a single parameter as before?

3 Answers

Welcome to SE!

Actually the error you encountered was undefined control sequence n (or words to that effect). n is evaluated in foreach which, in turn, is in a group. So, when the foreach is finished, n is forgotten. The trick is to make n in effect global while the foreach is running, as here:

documentclass[a4paper]{article}
usepackage{tikz}

newcommand{normaldot}[1]{ % Make a dot of fixed absolute size.
    node at #1 {normalsize $bullet$};
}

newcommand{plotperm}[1]{% plot a permutation
    foreach j [count=i] in {#1} {
        normaldot{(i,j)}{};
    };
}
newcommand{plotpermgrid}[1]{% plot a permutation in a grid
    foreach i [count=n] in {#1} {globalletnewn=n};  % Now newn stores the number of entries of the permutation globally. <<<--- 
    foreach i in {0,1,2,...,newn}{
        draw [color=darkgray] ({i+0.5}, 0.5)--({i+0.5}, {newn+0.5});
        draw [color=darkgray] (0.5, {i+0.5})--({newn+0.5}, {i+0.5});
    }
    plotperm{#1};
}

begin{document}
    begin{tikzpicture}
        plotpermgrid{2,4,1,3}
    end{tikzpicture}
end{document}  

The graphic produced is precisely what you posted.

Correct answer by sgmoye on March 23, 2021

You should instead compute the length of the input array:

documentclass[a4paper]{article}
usepackage{tikz}

newcommand{normaldot}[1]{ % Make a dot of fixed absolute size.
    node at #1 {normalsize $bullet$};
}

newcommand{plotperm}[1]{% plot a permutation
    foreach j [count=i] in {#1} {
        normaldot{(i,j)}{};
    };
}
newcommand{plotpermgrid}[1]{% plot a permutation in a grid
    pgfmathsetmacron{dim({#1})}% compute the length of the array
    foreach i in {0,1,...,n}{
        draw [color=darkgray] ({i+0.5}, 0.5)--({i+0.5}, {n+0.5});
        draw [color=darkgray] (0.5, {i+0.5})--({n+0.5}, {i+0.5});
    }
    plotperm{#1};
}

begin{document}

begin{tikzpicture}
  plotpermgrid{2,4,1,3}
end{tikzpicture}

end{document} 

Using {#1} transforms the argument in a properly delimited PGF array. The method has worked since TeX Live 2013 (TikZ/PGF version 3.0.0).

I believe that leaving n defined after foreach i [count=n] in {...}{...} has been deemed a bug and fixed, because all temporary control sequences used in a foreach loop should be local to the loop and not leave anything outside it (unless made global).


Having been informed (in a very unpleasant way, to be honest) that dim doesn't always behave as expected, although in your situation it should, provided the argument to plotpermgrid has at least two items, I provide a safer solution.

documentclass[a4paper]{article}
usepackage{tikz}
usepackage{xparse} % not needed with LaTeX release 2020-10-01 or later

ExplSyntaxOn
NewExpandableDocumentCommand{clistlen}{m}
 {
  clist_count:n { #1 }
 }
ExplSyntaxOff


newcommand{normaldot}[1]{ % Make a dot of fixed absolute size.
    node at #1 {normalsize $bullet$};
}

newcommand{plotperm}[1]{% plot a permutation
    foreach j [count=i] in {#1} {
        normaldot{(i,j)}{};
    };
}
newcommand{plotpermgrid}[1]{% plot a permutation in a grid
    pgfmathsetmacro{n}{clistlen{#1}}% get the number of items in #1
    foreach i in {0,1,...,n}{
        draw [color=darkgray] ({i+0.5}, 0.5)--({i+0.5}, {n+0.5});
        draw [color=darkgray] (0.5, {i+0.5})--({n+0.5}, {i+0.5});
    }
    plotperm{#1};
}

begin{document}

begin{tikzpicture}
  plotpermgrid{2,4,1,3}
end{tikzpicture}

end{document} 

You can even avoid to define n and use clistlen{#1} where n is used.

Answered by egreg on March 23, 2021

A wee bit different approach. Defined is only one new command which can be used in text, grid is drawn as nodes, no global sewing dimension of grid:

documentclass[margin=3mm]{standalone}
usepackage{tikz}
                         
newcommand{PPG}[1]% Plot a Permutation in a Grid
{
    begin{tikzpicture}[nodes={minimum size=1cm}]
foreach i [count=j] in {#1} %i: columns, j: rows
{
node at (j-0.5,i-0.5) {textbullet};
    foreach k in {1,...,j}
    {
    node[draw] at (j-0.5,k-0.5) {};
    node[draw] at (k-0.5,j-0.5) {};
    }
}
    end{tikzpicture}
}

begin{document}

PPG{2,4,1,3,5}
begin{tikzpicture}
PPG{2,4,1,3,5}
end{tikzpicture}

end{document} 

enter image description here

Addendum. If plot of termination in a grid (PPG) is always used in tikzpicture environment, for example:

begin{tikzpicture}
PPG{2,4,1,3,5}
end{tikzpicture}

then may be better that tikzpicture is removed from definition of PPG:

newcommand{PPG}[1]% Plot a Permutation in a Grid
{
    scoped[nodes={minimum size=1cm}]{
foreach i [count=j] in {#1} %i: columns, j: rows
{
node at (j-0.5,i-0.5) {textbullet};
    foreach k in {1,...,j}
    {
    node[draw] at (j-0.5,k-0.5) {};
    node[draw] at (k-0.5,j-0.5) {};
    }
}% end of foreach
                                    }% end  of scoped
}% end of newcommand

Answered by Zarko on March 23, 2021

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