4

I have a macro that prints exactly what I want, but I can't use it nested in another one. As far as I understand it is because the fist one is not completely executed and commands from first macro mixt together with those from the second one.

How to proceed ?

enter image description here

\documentclass{article}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\pgfplotsset{compat=1.9}

\makeatletter
\def\L@ste#1#2{
    \count255 = 1
    \loop   
    \def\n{\the\count255}%
    #1%
    \advance\count255 by 1
    \ifnum\count255>#2
    \else
    ,%
    \repeat
    }
\def\Liste#1#2{\L@ste{#1}{#2}}
\makeatother

\begin{document}

\verb+\Liste{\n-\n/(6-\n)*x}{5}+

Prints exactly what I need :

\Liste{\n-\n/(6-\n)*x}{5}

\bigskip    

But I can't use it directly :

\verb+max(\Liste{\n-\n/(6-\n)*x}{5})+

produces errors ...

\bigskip    

\begin{tikzpicture}

\begin{axis}[xmin=0,xmax=5,ymax=7]
  \addplot[color=black,smooth,samples=400] 
  {
% can't use it here, Why ?
    %max(\Liste{\n-\n/(6-\n)*x}{5})
max(1-1/(6-1)*x,2-2/(6-2)*x,3-3/(6-3)*x,4-4/(6-4)*x,5-5/(6-5)*x)
  };
\end{axis}
\end{tikzpicture}
\end{document}
6
  • This code makes no sense, sorry. For many reasons. One of them is that \else is not legal in \loop, but it's perhaps the least.
    – egreg
    Jun 15, 2014 at 10:25
  • @egreg Loop seems to ignore that as it works well, the result is correct. I don't understand then what you mean. By the way, how to test count255<=#2 as <= is forbiden and doesn't work ?
    – Tarass
    Jun 15, 2014 at 10:31
  • @egreg tug.org/TUGboat/tb08-2/tb18kabel.pdf Jun 15, 2014 at 10:40
  • @DavidCarlisle I know that paper; but this has the original \loop macro.
    – egreg
    Jun 15, 2014 at 10:48
  • @Tarass Your \Liste macro produces the instruction for printing what you'd like to feed to max; but you can't use it in the argument of max, because this wants the functions, not the instructions for printing them.
    – egreg
    Jun 15, 2014 at 10:51

2 Answers 2

11

The loop via \loop is not expandable, because it contains assignments. For the same reason \def\n{\the\count255 } (BTW, note the space) is not expandable as advancing the counter via \advance.

\Liste could be implemented in an expandable way:

  • Package intcalc helps in incrementing in an expandable way,
  • \n needs to be exchanged by the number in an expandable way.

But it is much easier to construct a macro (also token register) that contains the expanded list. In the following example \Liste generates the list and stores the result in macro \ListeResult. The latter can be used in max:

\documentclass{article}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\pgfplotsset{compat=1.9}

\makeatletter
\newcommand*{\Liste}[2]{%
  \count@ = 1 %
  \let\ListeResult\@empty
  \loop
    \def\n{\the\count@}%
    \edef\ListeResult{\ListeResult#1}%
    \advance\count@ by 1 %
  \ifnum#2<\count@
  \else
    \edef\ListeResult{\ListeResult,}%
  \repeat
}
\makeatother

\begin{document}

Prints exactly what I need :

\Liste{\n-\n/(6-\n)*x}{5}
\ListeResult

\bigskip

\begin{tikzpicture}

\begin{axis}[xmin=0,xmax=5,ymax=7]
  \Liste{\n-\n/(6-\n)*x}{5}
  \addplot[color=black,smooth,samples=400] {max(\ListeResult)};
\end{axis}
\end{tikzpicture}
\end{document}

Result

1
  • Great ! thank you for explantions and for the solution.
    – Tarass
    Jun 15, 2014 at 11:09
5

You can't feed \Liste to max, because it wants a syntactically correct list of functions, not the instructions for printing them.

Here's a working implementation; you must first compute the substitutions and define a macro to contain the list. Then you can use this macro in the argument of \max.

\documentclass{article}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\pgfplotsset{compat=newest}

\makeatletter
\newcommand\Liste[3][]{%
  \def\Liste@temp{\@gobble}%
  \count@=\z@
  \loop\ifnum\count@<#3\relax
  \advance\count@ by \@ne
  \begingroup\edef\n{\the\count@}%
  \edef\x{\endgroup\def\noexpand\Liste@temp{\Liste@temp,#2}}\x%
  \repeat
  \if\relax\detokenize{#1}\relax
    \Liste@temp
  \else
    \let#1=\Liste@temp
  \fi
}
\makeatother

\begin{document}

\Liste{\n-\n/(6-\n)*x}{5}

\bigskip    

\begin{tikzpicture}

\begin{axis}[xmin=0,xmax=5,ymax=7]
   \Liste[\foo]{\n-\n/(6-\n)*x}{5}
  \addplot[color=black,smooth,samples=400] 
  {
    max(\foo)
  };
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

A new implementation with expl3.

\documentclass{article}
\usepackage{geometry}
\usepackage{xparse}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\pgfplotsset{compat=newest}

\ExplSyntaxOn
\NewDocumentCommand{\addplotlist}{smO{1}mmO{}}
 {% #1 = * if printing
  % #2 = function to apply to the list
  % #3 = starting point, default 1
  % #4 = end point
  % #5 = list building function
  % #6 = options for addplot
  \cs_set:Nn \__tarass_process:n { ,#5 }
  \clist_set:Nx \l__tarass_process_clist
   {
    \int_step_function:nnnN { #3 } { 1 } { #4 } \__tarass_process:n
   }
  \IfBooleanTF{#1}
   {
    $\use:c{#2}(\clist_use:Nn \l__tarass_process_clist { ,~ })$
   }
   {
    \addplot[#6]{ #2(\l__tarass_process_clist) }
   }
 }
\clist_new:N \l__tarass_process_clist
\ExplSyntaxOff

\begin{document}

\addplotlist*{max}{5}{#1-#1/(6-#1)*x}

\addplotlist*{min}{5}{#1+#1/(6-#1)*x}

\bigskip

\begin{tikzpicture}
\begin{axis}[xmin=0,xmax=5,ymax=7]
  \addplotlist{max}{5}{#1-#1/(6-#1)*x}[color=black,smooth,samples=400];
  \addplotlist{min}{5}{#1+#1/(6-#1)*x}[color=red,smooth,samples=400];
\end{axis}
\end{tikzpicture}
\end{document}

The variable parameter is denoted by #1. The * version prints the obtained list.

enter image description here

1
  • Thank you for this answer. It is much harder to understand for me than Heiko's one. Could you please, on spair time, add some comments explaining the purpose of the different commands ? I am very interested in. Of course I'll add a bounty on it.
    – Tarass
    Jun 15, 2014 at 11:22

You must log in to answer this question.

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