6

I'm using a macro I've defined based on some suggestions here to automate the creation of tables.

For example something like

\newcommand{\clearrows}{\let\matrixcontent\empty}
\newcommand{\actionsrow}[3]{%
  \foreach \i in #3 {%
    \pgfmathparse{#1[\i-1]}
    \let\c\pgfmathresult
    \pgfmathparse{#2[\i-1]}
    \let\s\pgfmathresult
    \begingroup\edef\x{\endgroup
       \noexpand\gappto\noexpand\matrixcontent{\noexpand\node[\s]{\c}; \&}}\x
    }%
  \gappto\matrixcontent{\\}%
}

lets me easily create the tables I need, with a few lines of code. For example,

\newcommand{\rowhv}[1]{
    \actionsrow{{"$e$","$h$","$v$","$hv$"}}{{{"fill=gray!3"},{"fill=gray!33"},{"fill=gray!67"},{"fill=gray!100"}}}{#1} }
\clearrows
\rowhv{{1,2,3,4}} \rowhv{{2,1,4,3}} \rowhv{{3,4,1,2}} \rowhv{{4,3,2,1}}

\begin{tikzpicture}
    \matrix[ampersand replacement=\&,nodes={minimum size=7mm}]{\matrixcontent};
\end{tikzpicture}

produces

enter image description here

which may not seem like much of an accomplishment, but I need to build quite large tables, and a lot of them, so these macros are essential.

I need to be able to put arbitrary content in the cells, ranging from arbitrary formatting (e.g., \textsf{..}) to imported images (using, for example, \includegraphics) or other macros I define to draw node contents. But the code above only works for cell contents that are plain text. If I supply anything other other than plain text among the items in the first argument to \actionsrow, which is used for \c in the loop there, I get an error,

Argument of \reserved@a has an extra }.

even though such content is perfectly valid for nodes. I suspect there's something going on with the way my (pilfered) macro is parsing \c, but I don't even know where to begin track that down.

How can I modify my \actionsrow macro so that I can specify arbitrary LaTeX as contents for my nodes?


\documentclass[]{article}

\usepackage{etoolbox}
\usepackage{tikz}
\usetikzlibrary{matrix}


\begin{document}

% See -- https://tex.stackexchange.com/a/47649/7844
\newcommand{\clearrows}{\let\matrixcontent\empty}
\newcommand{\actionsrow}[3]{%
  \foreach \i in #3 {%
    \pgfmathparse{#1[\i-1]} % See -- https://tex.stackexchange.com/a/56838/7844; note \let vs \def
    \let\c\pgfmathresult
    \pgfmathparse{#2[\i-1]}
    \let\s\pgfmathresult
    \begingroup\edef\x{\endgroup
       \noexpand\gappto\noexpand\matrixcontent{\noexpand\node[\s]{\c}; \&}}\x
    }%
  \gappto\matrixcontent{\\}%
}

% This works fine
\newcommand{\rowhv}[1]{
    \actionsrow{{"$e$","$h$","$v$","$hv$"}}{{{"fill=gray!3"},{"fill=gray!33"},{"fill=gray!67"},{"fill=gray!100"}}}{#1} }
\clearrows
\rowhv{{1,2,3,4}} \rowhv{{2,1,4,3}} \rowhv{{3,4,1,2}} \rowhv{{4,3,2,1}}

\begin{center}
\begin{tikzpicture}
  \matrix[ampersand replacement=\&,nodes={minimum size=7mm}]{\matrixcontent};
\end{tikzpicture}
\end{center}

% Here, arbitrary specifications work for the nodes, if done manually
\begin{center}
\begin{tikzpicture}
\matrix [nodes={minimum size=7mm}]
{
    \node[fill=gray!3]{\texttt{e}}; & \node[fill=gray!33]{\color{red}$h$}; & \node[fill=gray!67]{$v$}; & \node[fill=gray!100]{$hv$}; \\
    \node[fill=gray!33]{\color{red}$h$}; & \node[fill=gray!3]{\texttt{e}}; & \node[fill=gray!100]{$hv$}; & \node[fill=gray!67]{$v$}; \\
    \node[fill=gray!67]{$v$}; & \node[fill=gray!100]{$hv$}; & \node[fill=gray!3]{\texttt{e}}; & \node[fill=gray!33]{\color{red}$h$}; \\
    \node[fill=gray!100]{$hv$}; & \node[fill=gray!67]{$v$}; & \node[fill=gray!33]{\color{red}$h$}; & \node[fill=gray!3]{\texttt{e}}; \\
 };
\end{tikzpicture}
\end{center}

% But this fails
\renewcommand{\rowhv}[1]{
    \actionsrow{{"\texttt{e}","\color{red}$h$","$v$","$hv$"}}{{{"fill=gray!3"},{"fill=gray!33"},{"fill=gray!67"},{"fill=gray!100"}}}{#1} }
\clearrows
\rowhv{{1,2,3,4}} \rowhv{{2,1,4,3}} \rowhv{{3,4,1,2}} \rowhv{{4,3,2,1}}

\begin{center}
\begin{tikzpicture}
  \matrix[ampersand replacement=\&,nodes={minimum size=7mm}]{\matrixcontent};
\end{tikzpicture}
\end{center}

\end{document}
2
  • I'd appreciate help titling and tagging this question: I may not have the source of my problem properly identified.
    – orome
    Jun 26, 2012 at 3:50
  • 1
    I've changed them to what seems to be an accurate description of the issue.
    – Ryan Reich
    Jun 26, 2012 at 6:10

1 Answer 1

6

You have two problems, both with \edef. The second one is literally with your \edef, as any macro inside #1 that finds its way into \c will be expanded into \x and create chaos. The first one is with \pgfmathparse, as according to the manual, everything in a math expression is fully expanded, and therefore, you will require extra protection for things that should not be expanded. I see two ways around this:

  1. Put \unexpanded around every item in array #1 manually, and also put \expandonce in front of \c inside that \edef (so it is expanded but its contents are not). This is a pain but will fix the problem.

  2. Do a preliminary loop with \foreach to put in those \unexpandeds before continuing with the current body of \actionsrow. Since it will be a pain to get the double quotes to interact well with this, I suggest you make the syntax for its first argument {$a$, $\mathbf{b}$, \includegraphics{c.pdf}, ...}, without the quotes, and have \foreach add both the \unexpanded and the quotes. Also, don't forget \expandonce\c in the \edef, as before.

Here is how I implemented option 2:

\newcommand{\actionsrow}[3]{%
  %%%
  % NEW STUFF
  %%%
  \let\comma=\empty
  \let\b=\empty
  \foreach \a in {#1} {%
   \xappto\b{\comma"\noexpand\unexpanded{\expandonce\a}"}%
   \gdef\comma{,}%
  }%
  \foreach \i in #3 {%
    %%% CHANGED #1 -> \b
    \pgfmathparse{{\b}[\i-1]}%
    \let\c\pgfmathresult
    \pgfmathparse{#2[\i-1]}%
    \let\s\pgfmathresult
    \begingroup\edef\x{\endgroup
       %%% ADDED \expandonce\c
       \noexpand\gappto\noexpand\matrixcontent{\noexpand\node[\s]{\expandonce\c}; \&}}\x
    }%
  \gappto\matrixcontent{\\}%
}

\newcommand{\rowhv}[1]{
 \actionsrow
  %%% NEW SYNTAX: first arg is a pure CSV list
  {$\mathbf{e}$,$h$,$v$,$hv$}
  {{{"fill=gray!3"},{"fill=gray!33"},{"fill=gray!67"},{"fill=gray!100"}}}
  {#1}%
}
5
  • Thanks, this does a great job. I still have some issues with certian arguments (e.g. \color{..}: "Undefined color 'r'"). It also looks like I need to do some reading on how expansion works.
    – orome
    Jun 26, 2012 at 14:36
  • What problems are you having with color? If I put \color{red} into the $h$, for example, it works just fine.
    – Ryan Reich
    Jun 26, 2012 at 16:05
  • Interesting: it works inside the $..$, but not outside: \color{red}h and \color{red}$h$ fail, but \color{red}$h$ works.
    – orome
    Jun 26, 2012 at 19:14
  • I also don't seem to be able to provide any arguments that use the scope environment (e.g. many figures I'd like do draw and place in the cells).
    – orome
    Jun 26, 2012 at 19:42
  • This seems to be a problem (likely a bug) with the way pgfmath treats arrays. Try \pgfmathparse{{"{red}a", "b"}[0]}\pgfmathresult and cringe at the result. It actually catches the "nested array" {red}; apparently, no braces are safe in array elements.
    – Ryan Reich
    Jun 26, 2012 at 23:10

You must log in to answer this question.

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