4

People!

I want to give the letter format, i.e., \tiny, as parameter for a macro \printarray that calls another macro \subprintarray. With the given code below I expect such a result:

enter image description here

But I get following:

enter image description here

I do not why the letter format is lost in the second macro call. I use the technique of \protected@edef that egreg showed me in a similar question

Here is my code:

\documentclass{letter}
\usepackage{keycommand}
\usepackage{tikz}

\makeatletter
\newkeycommand{\printarray}[myarray={"a","3.4","more", "Test"},arrayformat={\small}, n=3]
{
    \edef\tagsaux{\commandkey{myarray}};
    \edef\naux{\commandkey{n}};
    \protected@edef\formataux{\commandkey{arrayformat}};

    \subprintarray[submyarray=\tagsaux, subarrayformat={\formataux}, subn=\naux]
}

\newkeycommand{\subprintarray}[submyarray={"a","3.4","$sin(90)+4$"},subarrayformat={\small}, subn=3]
{   
    \protected@edef\subtags{\commandkey{submyarray}};

    \foreach \xpos in {1,2,...,\commandkey{subn}}{
        \path (1.5*\xpos, 1) node { \pgfmathparse{\subtags[\xpos]}\pgfmathresult };
        \path (1.5*\xpos, 0) node { \commandkey{subarrayformat} \pgfmathparse{\subtags[\xpos]}\pgfmathresult };
    }
}
\makeatother

\begin{document}
\def\tags{{"ABC","b","$\sin(20)+4$","111","4","5","6","7"}}

Call to \texttt{\textbackslash subprintarray}\\
\fbox{
\begin{tikzpicture}
    \subprintarray[submyarray=\tags,subarrayformat={\small },subn=4]
\end{tikzpicture}}

Call to \texttt{\textbackslash printarray}\\
\fbox{   
\begin{tikzpicture}
    \printarray[myarray=\tags,arrayformat={\tiny}, n=4]
\end{tikzpicture}}

\end{document}

I use TikZ-pgf to perform a loop and deal with an array but I don't think that is the problem.

Please do not change the macro calls but modify only inside the macros. (Explanations to better understand the problem/solution are pretty welcome)

Thanks a lot!

  • 2
    \tiny is a macro that doesn't survive \protected@edef and should be prefixed by \protect in case it eventually ends up in it. – egreg Oct 21 '15 at 21:23
  • @egreg : yes it worked writing \protect\tiny as a parameter. Quite practical. Thanks! When I was trying I discovered than \tiny\normalfont also works the desire result, although is inelegant. At the end I found Guho's solution, the \expandafter\noexpand in the macro code, the best compromise between practical and elegant. Thanks for your fast answers. My code looks good and it works! :-) – loved.by.Jesus Oct 23 '15 at 9:26
4

As far as I can tell, this issue stems from the \tiny option being expanded prematurely in your \protected@edef\formataux{\commandkey{arrayformat}}; line in \printarray. Adding \expandafter\noexpand before \command{arrayformat} to limit the expansion appears to fix it (I end up with your expected result). Adding \noexpand in front of tiny in the call works as well. I'm guessing this is a byproduct of the approach keycommand uses to store the parsed options (\commandkey{arrayformat} expands to \csname keycmd->printarray@arrayformat\endcsname).

I'd recommend using pgfkeys as the behavior is much more intuitive and flexible. Below is your example implemented with pgfkeys - macro calls are unmodified, as requested. In it, the options in \printarray are parsed and the arrayformat is stored directly in \formataux (i.e., \formataux expands to \tiny, as expected):

Edit: Thanks to @cfr for pointing out that the \pgfkeys can be all defined in a single call and that \newcommand{\tags} is a better approach than \def\tags (the former will throw an error if \tags is already defined):

\documentclass{letter}
\usepackage{keycommand}
\usepackage{tikz}
\pgfkeys{/tikz/.cd,%
    myarray/.initial={"a","3.4","more", "Test"},
    myarray/.get=\tagsaux,
    myarray/.store in=\tagsaux,
    arrayformat/.initial={\small},
    arrayformat/.get=\formataux,
    arrayformat/.store in=\formataux,
    n/.initial=3,
    n/.get=\naux,
    n/.store in=\naux,
    submyarray/.initial={"a","3.4","$sin(90)+4$"},
    submyarray/.get=\subtagsaux,
    submyarray/.store in=\subtagsaux,
    subarrayformat/.initial={\small},
    subarrayformat/.get=\subformataux,
    subarrayformat/.store in=\subformataux,
    subn/.initial=3,
    subn/.get=\subnaux,
    subn/.store in=\subnaux}

\makeatletter
\newcommand{\printarray}[1][]{%
    \tikzset{#1}
    \subprintarray[submyarray=\tagsaux,subarrayformat=\formataux,subn=\naux]    
    }
\newcommand{\subprintarray}[1][]{%
    \tikzset{#1}
    \foreach \xpos in {1,2,...,\subnaux}{
        \path (1.5*\xpos, 1) node { \pgfmathparse{\subtagsaux[\xpos]}\pgfmathresult };
        \path (1.5*\xpos, 0) node { \subformataux \pgfmathparse{\subtagsaux[\xpos]}\pgfmathresult };
    }
}
\makeatother

\begin{document}
\newcommand{\tags}{{"ABC","b","$\sin(20)+4$","111","4","5","6","7"}}

Call to \texttt{\textbackslash subprintarray}\\
\fbox{
\begin{tikzpicture}
    \subprintarray[submyarray=\tags,subarrayformat={\small },subn=4]
\end{tikzpicture}}

Call to \texttt{\textbackslash printarray}\\
\fbox{   
\begin{tikzpicture}
    \printarray[myarray=\tags,arrayformat={\tiny }, n=4]
\end{tikzpicture}}

\end{document}

enter image description here

  • 2
    Very nice example of using arrays in PGF keys (which I find more-or-less incomprehensible in general). Why 2 calls to \pgfkeys, though? Couldn't you set all the keys with a single call? It would be better, if possible, to avoid use of \def in LaTeX since it does no checking to ensure it is not overwriting a command. Especially since \tags seems like just the kind of thing a package might define.... – cfr Oct 21 '15 at 21:14
  • @cfr: Indeed you can, thanks for the catch. That was left over from the stepwise adaptation to pgfkeys. Answer updated. – Guho Oct 21 '15 at 21:19
  • @cfr: Another great point. I've updated the answer to reflect that. – Guho Oct 21 '15 at 21:24
  • @guho, @cfr: you really worked out an elegant answer. At the moment I am using the package keycommand (which is more for dummies ;-)) but, in the future, I am going to use \pgfkeys, it looks great. Thanks a lot! – loved.by.Jesus Oct 23 '15 at 9:34
4

Some commands don't survive even \protected@edef and among them there are the size changing commands, which are not supposed to be in moving arguments (a moving argument is one that's subject to \protected@edef or \protected@write, usually sectioning commands or captions).

If they are in such moving arguments they must be prefixed by \protect.

Here's a different strategy, that avoids keycommand with its problems and rather uses expl3.

\documentclass{letter}
\usepackage{xparse}
\usepackage{tikz}

\ExplSyntaxOn
\keys_define:nn { lbj/printarray }
 {
  myarray     .code:n      = \clist_set:No \l_lbj_printarray_myarray_clist { #1 },
  arrayformat .tl_set:N    = \l_lbj_printarray_arrayformat_tl,
  n           .int_set:N   = \l_lbj_printarray_n_int,
  myarray     .initial:n   = {"a","3.4","more", "Test"},
  arrayformat .initial:n   = \small,
  n           .initial:n   = 3,
 }
\keys_define:nn { lbj/subprintarray }
 {
  submyarray     .code:n      = \clist_set:No \l_lbj_printarray_submyarray_clist { #1 },
  subarrayformat .tl_set:N    = \l_lbj_printarray_subarrayformat_tl,
  subn           .int_set:N   = \l_lbj_printarray_subn_int,
  submyarray     .initial:n   = {"a","3.4","more", "$\sin(90)+4$"},
  subarrayformat .initial:n   = \small,
  subn           .initial:n   = 3,
 }

\NewDocumentCommand{\printarray}{ O{} }
 {
  \group_begin:
  \keys_set:nn { lbj/printarray } { #1 }
  \subprintarray[
   submyarray=\l_lbj_printarray_myarray_clist,
   subarrayformat=\l_lbj_printarray_arrayformat_tl,
   subn=\l_lbj_printarray_n_int,
  ]
  \group_end:
}

\NewDocumentCommand{\subprintarray}{ O{} }
 {
  \keys_set:nn { lbj/subprintarray } { #1 }
  \int_step_inline:nnnn {1} {1} {\l_lbj_printarray_subn_int}
   {
    \path (1.5*##1, 1) node {
      \strut
      \clist_item:Nn \l_lbj_printarray_submyarray_clist { ##1 }
    };
    \path (1.5*##1, 0) node {
      \l_lbj_printarray_subarrayformat_tl \strut
      \clist_item:Nn \l_lbj_printarray_submyarray_clist { ##1 }
    };
   }
 }
\ExplSyntaxOff

\begin{document}

\newcommand\tags{"ABC","b","$\sin(20)+4$","111","4","5","6","7"}

Call to \verb|\subprintarray|\\
\fbox{%
  \begin{tikzpicture}
    \subprintarray[submyarray=\tags,subarrayformat={\small },subn=4]
  \end{tikzpicture}}

Call to \verb|\printarray|\\
\fbox{%
  \begin{tikzpicture}
    \printarray[myarray=\tags,arrayformat={\tiny}, n=4]
  \end{tikzpicture}}

\end{document}

The keys are defined in advance and then set by the command with \keys_set:nn. You can see that it's easy to pass the values from \printarray to \subprintarray. Also extracting an item from a clist variable is easier than with \pgfmathparse.

Instead of \foreach one can use \int_step_inline:nnnn that has no special marker; the current value is simply denoted by #1 (it must be ##1 here because it is inside a definition).

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.