4

I wanted to distinguish the control variables of different agents in a separate color so that my students can read examples more easily. To do so, I wanted to write a macro \definevar{x,y;z} that I could call at the beginning of each example to define \var before the semicolon as {\color{player1} var} and after the semicolon as {\color{player2} var}. I found in this answer how to do it for a single variable, but was surprised that this method did not extend to lists processed by the listofitems package in a straightforward manner.

In below code, \defvar works well for a single variable, but somehow \definevar does not define the variables. If I replace \foreach with pgfplot's \pgfplotsforeachungrouped, it defines the variables but initializes them all to the last input variable.

\documentclass{article}
\usepackage{xcolor, pgffor, listofitems}

\colorlet{player1}{blue}
\colorlet{player2}{red}

\newcommand{\defvar}[2]{
    \expandafter\def\csname #2\endcsname{{\color{player#1} #2}}
}

\newcommand{\definevar}[1]{
    \setsepchar{;/,}%
    \greadlist*\varlist{#1}%
    \foreach \pl in {1, ..., \varlistlen} {%
        \foreachitem \i \in \varlist[\pl] {%
            \defvar{\pl}{\i}%
        }%
    }%
}

\begin{document}

\defvar{1}{x}
\defvar{2}{y}
$\x + \y = 1$.

\definevar{x, y; z}
$\x + \y = \z$.

\end{document}

2 Answers 2

3

Several issues:

  1. \defvar needs to employ global \gdef rather than \def, since it is invoked inside of the loop groups of \definevar.

  2. \pl and \i need to be once expanded before being used to call \defvar. Without this, the \defvar retains the literal \pl and \i rather than using their replacement texts that you desire.

The MWE repaired:

\documentclass{article}
\usepackage{xcolor, pgffor, listofitems}

\colorlet{player1}{blue}
\colorlet{player2}{red}

\newcommand{\defvar}[2]{
    \expandafter\gdef\csname #2\endcsname{{\color{player#1} #2}}
}

\newcommand{\definevar}[1]{
    \setsepchar{;/,}%
    \greadlist*\varlist{#1}%
    \foreach \pl in {1, ..., \varlistlen} {%
        \foreachitem \i \in \varlist[\pl] {%
            \def\tmp{\expandafter\defvar\expandafter{\pl}}%
            \expandafter\tmp\expandafter{\i}%
        }%
    }%
}

\begin{document}

\defvar{1}{x}
\defvar{2}{y}
$\x + \y = 1$.

\definevar{x, y; z}
$\x + \y = \z$.

\end{document}

enter image description here

An alternate way to achieve those same goals is to use \xdef inside of \defvar instead of \gdef (as long as you \noexpand the \color). That way, inside of \definevar, you can revert to the simpler syntax, without worrying about expansions.

\documentclass{article}
\usepackage{xcolor, pgffor, listofitems}

\colorlet{player1}{blue}
\colorlet{player2}{red}

\newcommand{\defvar}[2]{
    \expandafter\xdef\csname #2\endcsname{{\noexpand\color{player#1} #2}}
}

\newcommand{\definevar}[1]{
    \setsepchar{;/,}%
    \greadlist*\varlist{#1}%
    \foreach \pl in {1, ..., \varlistlen} {%
        \foreachitem \i \in \varlist[\pl] {%
            \defvar{\pl}{\i}%
        }%
    }%
}

\begin{document}

\defvar{1}{x}
\defvar{2}{y}
$\x + \y = 1$.

\definevar{x, y; z}
$\x + \y = \z$.

\end{document}
2
  • Beautiful. I think with this example I have finally understood when precisely to use expandafter. Commented Apr 5, 2021 at 12:17
  • @S.Olafsson The \expandafter ken comes suddenly, like a light bulb! I know exactly what you mean. Commented Apr 5, 2021 at 12:42
2

You can use expl3 so you don't need scratch macros.

The argument to \definevar is first split into pieces at semicolons, then each piece is examined to produce variables colored according to the piece number, you just need to define as many colors playern as you need.

\documentclass{article}
\usepackage{xcolor}

\colorlet{player1}{blue}
\colorlet{player2}{red}
\colorlet{player3}{green!60!blue}

\ExplSyntaxOn

\NewDocumentCommand{\definevar}{m}
 {
  \seq_set_split:Nnn \l_tmpa_seq { ; } { #1 }
  \seq_map_indexed_inline:Nn \l_tmpa_seq
   {
    \clist_map_inline:nn { ##2 }
     {
      \cs_set_protected:cpn { ####1 } { \textcolor{player##1}{####1} }
     }
   }
 }

\ExplSyntaxOff

\begin{document}

$\definevar{x;y}\x + \y = 1$.

$\definevar{x,y;z;t}\x+\y=\z-\t$.

\end{document}

enter image description here

You might also consider the following:

\documentclass{article}
\usepackage{xcolor}

\colorlet{player1}{blue}
\colorlet{player2}{red}
\colorlet{player3}{green!60!blue}

\ExplSyntaxOn

\NewDocumentCommand{\definevar}{m}
 {
  \seq_set_split:Nnn \l_tmpa_seq { ; } { #1 }
  \seq_map_indexed_inline:Nn \l_tmpa_seq
   {
    \clist_map_inline:nn { ##2 }
     {
      \cs_set_protected:cx { __olafsson_var_####1: }
       {
        \exp_not:N \textcolor{player##1}{\mathchar\the\mathcode`####1}
       }
      \char_set_active_eq:Nc ####1 { __olafsson_var_####1: }
      \mathcode `####1 = "8000 \scan_stop:
     }
   }
 }

\ExplSyntaxOff

\begin{document}

$\definevar{x;y} x + y = 1$.

$\definevar{x,y;z;t} x + y = z - t$.

\end{document}

that yields the same output.

2
  • Nice, although the number of # is quite scary... ;-)
    – Rmano
    Commented Apr 5, 2021 at 20:58
  • @Rmano At each level you double them… ;-)
    – egreg
    Commented Apr 5, 2021 at 21:09

You must log in to answer this question.

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