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 this
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?
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}
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
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP