3

In the MWE below, I want to create a \doPlotCoords macro from scratch, by appending \addplot commands:

\documentclass{article}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{adjustbox}

\usepackage{tikz}
\usetikzlibrary{pgfplots.groupplots}
\usetikzlibrary{shapes}
\usetikzlibrary{positioning}
\usetikzlibrary{decorations.pathreplacing}

\xdef\doPlotCoords{}
\foreach \r in {0,1,...,10}{%
  \xdef\tempCoords{} %
  \foreach \c in {0,1,...,10}{ %
    \xdef\tempCoords{\tempCoords (\r,\c)\space}
  } % end \foreach \c
  % just \xdef concatenating gives "! Undefined control sequence." for \addplot; use \protected@edef
%   \xdef\doPlotCoords{ \doPlotCoords
%   \noexpand\addplot+[color=blue,mark=*,mark options={blue},] coordinates{ \tempCoords };}
  % with "\protected@xdef", I get "! You can't use a prefix with `the character @'.":
  \makeatletter %
  \protected@xdef\doPlotCoords{ \doPlotCoords
  \protect\addplot+[color=blue,mark=*,mark options={blue},] coordinates{ \tempCoords };}
  \makeatother
} % end \foreach \r

\begin{document}
\show\doPlotCoords
\end{document}

Eventually, I'd like the \doPlotCoords macro to contain:

\addplot+[color=blue,mark=*,mark options={blue},] coordinates{ (0,0) (0,1) ... };
\addplot+[color=blue,mark=*,mark options={blue},] coordinates{ (1,0) (1,1) ... };
...

... so I could just use \doPlotCoords as a "subroutine" to execute all those \addplots; however, currently it fails with:

! You can't use a prefix with `the character @'.
<to be read again> 
                   @
l.26 }
       % end \foreach \r

I think the main problem is that: upon the first \xdef, \protect/\noexpand may be obeyed; but when concatenating, the \doPlotCoords already contains the \addplot, so when the \xdef tries to expand \doPlotCoords, it also wants to expand the \addplot inside. But I cannot really see what the "You can't use a prefix with `the character @'." should refer to, when using \protected@xdef (got that via Minimal \protected@edef example).

So how could I build a macro using a \edef\mymacro{\mymacro something else} type concatenation, such that the protected tokens inside \mymacro are not expanded when the \edef runs multiple times on \mymacro? (I kind of think that this "multiple times" might justify calling this approach "recursive macro concatenation"; but I'm not sure, as this isn't a "real" recursion of the function-calling-itself kind...)

0

3 Answers 3

4

The problem you're experiencing is similar to the following minimal example:

\documentclass{article}
\newcommand{\zz}[1]{%
  \makeatletter%
  \protected@edef\zzz{#1}%
  \makeatother%
}
\begin{document}

abc\zz{zz}

\end{document}

You try to be concise in your macro programming, using a \makeatletter...\makeatother pair only when it's necessary. In this case, just around the macro using an @ symbol - \protected@edef. However, by the time you declare the definition of \zz, the category codes for its replacement text (everything inside the braced group) has already been set. That is, the \makeatletter...\makeatother pair has no effect on correctly interpreting the @ in \protected@edef.

The solution is to use the \makeatletter...\makeatother pair outside the definition:

\documentclass{article}
\makeatletter%
\newcommand{\zz}[1]{%
  \protected@edef\zzz{#1}%
}
\makeatother%
\begin{document}

abc\zz{zz}

\end{document}

The same goes for the \foreach loops you create. They are in essence also just macros that take arguments in order to delimit the scope. So, once these arguments are read (as a whole), category code changes have no effect on its contents. Place the \makeatletter...\makeatother pair outside the outermost (lowest) group (\foreach in your case in order to use @-like macros properly:

\makeatletter
\foreach ... {
  ...
  \foreach ... {
    ...
  }
  ...
}
\makeatother
1
  • Many thanks for the detailed exposition @Werner, great to have this reference. Cheers!
    – sdaau
    Jun 2, 2014 at 23:29
6

You can't use \makeatletter inside the argument of another command (for same reason you can not use \verb there, but you don't really need @ commands here anyway:

\documentclass{article}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{adjustbox}

\usepackage{tikz}
\usetikzlibrary{pgfplots.groupplots}
\usetikzlibrary{shapes}
\usetikzlibrary{positioning}
\usetikzlibrary{decorations.pathreplacing}

 \xdef\doPlotCoords{}
{\let\addplot\relax
\foreach \r in {0,1,...,10}{%
  \xdef\tempCoords{}%
  \foreach \c in {0,1,...,10}{%
    \xdef\tempCoords{\tempCoords (\r,\c)\space}
  }

 \xdef\doPlotCoords{\doPlotCoords
  \addplot+[color=blue,mark=*,mark options={blue},] coordinates{ \tempCoords };}
}% end \foreach \r
}


\begin{document}
\show\doPlotCoords
\end{document}
2
  • Many thanks for the answer, @DavidCarlisle - I remember I tried this example with \xdef originally, but I cannot recall what the problem was, but it made me look further, and at that point I found \protected@xdef and I used it; good to know it isn't really needed. Cheers!
    – sdaau
    Jun 2, 2014 at 23:32
  • 1
    @sdaau you probably didn't do \let\addplot\relax Jun 2, 2014 at 23:34
5

You may enjoy an expl3 implementation. The macro \dosteps is a wrapper around \int_step_inline:nnnn; the syntax is

\dostep{<initial>}[<step>]{<final>}{<code>}

where the step is optional (default 1). In the <code>, the current value is available as #1. If a \dosteps is called inside <code>, for the inner cycle the current value is available as ##1 (and so on, doubling the number of #).

The macro \appendto appends the second argument to the parameterless macro given as first argument; the *-variant does complete expansion. In order to avoid using \noexpand\addplot, I defined a \paddplot macro that's essentially the same, but protected under expansion.

\documentclass{article}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{adjustbox}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{pgfplots.groupplots}
\usetikzlibrary{shapes}
\usetikzlibrary{positioning}
\usetikzlibrary{decorations.pathreplacing}

\ExplSyntaxOn
\NewDocumentCommand{\dosteps}{mO{1}mm}
 {%#1=start, #2=step (default 1), #3=end, #4=code
  \int_step_inline:nnnn {#1}{#2}{#3}{#4}
 }
\NewDocumentCommand{\appendto}{smm}
 {%#1=*, #2=token list, #3=code
  \IfBooleanTF{#1}{\tl_put_right:Nx}{\tl_put_right:Nn} #2 { #3 }
 }
\ExplSyntaxOff

\newcommand\tempCoords{} % initialize
\newcommand\doPlotCoords{}
\NewDocumentCommand{\paddplot}{}{\addplot}% protected version of \addplot

\dosteps{0}{10}{%
  \renewcommand{\tempCoords}{}% reinitialize
  \dosteps{0}{10}{%
    \appendto\tempCoords{(#1,##1) }% #1 is the outer cycle, ##1 the inner one
  }
  \appendto*\doPlotCoords{%
    \paddplot+[color=blue,mark=*,mark options={blue},] coordinates { \tempCoords };
  }%
}

\begin{document}
\show\doPlotCoords
\end{document}

This is the output on the terminal for the final \show command:

> \doPlotCoords=macro:
->\addplot +[color=blue,mark=*,mark options={blue},] coordinates { (0,0) (0,1) 
(0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9) (0,10) }; \addplot +[color=blue
,mark=*,mark options={blue},] coordinates { (1,0) (1,1) (1,2) (1,3) (1,4) (1,5)
 (1,6) (1,7) (1,8) (1,9) (1,10) }; \addplot +[color=blue,mark=*,mark options={b
lue},] coordinates { (2,0) (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7) (2,8) (2,9
) (2,10) }; \addplot +[color=blue,mark=*,mark options={blue},] coordinates { (3
,0) (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7) (3,8) (3,9) (3,10) }; \addplot +[
color=blue,mark=*,mark options={blue},] coordinates { (4,0) (4,1) (4,2) (4,3) (
4,4) (4,5) (4,6) (4,7) (4,8) (4,9) (4,10) }; \addplot +[color=blue,mark=*,mark 
options={blue},] coordinates { (5,0) (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) 
(5,8) (5,9) (5,10) }; \addplot +[color=blue,mark=*,mark options={blue},] coordi
nates { (6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7) (6,8) (6,9) (6,10) }; \
addplot +[color=blue,mark=*,mark options={blue},] coordinates { (7,0) (7,1) (7,
2) (7,3) (7,4) (7,5) (7,6) (7,7) (7,8) (7,9) (7,10) }; \addplot +[color=blue,ma
rk=*,mark options={blue},] coordinates { (8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8
,6) (8,7) (8,8) (8,9) (8,10) }; \addplot +[color=blue,mark=*,mark options={blue
},] coordinates { (9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) (
9,10) }; \addplot +[color=blue,mark=*,mark options={blue},] coordinates { (10,0
) (10,1) (10,2) (10,3) (10,4) (10,5) (10,6) (10,7) (10,8) (10,9) (10,10) }; .
1
  • Many thanks for the answer, @egreg - stuff like this will make me want study Latex 3 soon :) Cheers!
    – sdaau
    Jun 3, 2014 at 0:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .