TransWikia.com

Define macros from a list processed by the listofitems package

TeX - LaTeX Asked on April 12, 2021

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]{
    expandafterdefcsname #2endcsname{{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

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]{
    expandaftergdefcsname #2endcsname{{color{player#1} #2}}
}

newcommand{definevar}[1]{
    setsepchar{;/,}%
    greadlist*varlist{#1}%
    foreach pl in {1, ..., varlistlen} {%
        foreachitem i in varlist[pl] {%
            deftmp{expandafterdefvarexpandafter{pl}}%
            expandaftertmpexpandafter{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]{
    expandafterxdefcsname #2endcsname{{noexpandcolor{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}

Correct answer by Steven B. Segletes on April 12, 2021

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}{mathcharthemathcode`####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.

Answered by egreg on April 12, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP