2

This is a followup to Egregs answer in the question Expandable math, The code is included here as an MWE.

It would be a great help if I could somehow delete all previeously defined variables, or assign them only within a group. Another possibility could be simply to allow reassigning of variables with the same name. As of now, this gives an error on attempting to do so.

Edit: I've added some lines to the MWE at the bottom, which tries to set the variables within a group(but still makes them global), attempting to redefine a variable, which gives an error on trying to print it.

Error given:

./root.tex:122: Missing \endcsname inserted.

                \cs_set_nopar:Npx 

l.122 }

\documentclass{article}
\usepackage{xparse,l3regex,siunitx,xcolor}

\ExplSyntaxOn

\seq_new:N \g_runart_variables_seq
\prop_new:N \l__runart_variables_temp_prop
\tl_new:N \l__runart_variables_matte_tl
\tl_new:N \l__runart_variables_item_tl

\NewDocumentCommand{\definevariable}{mm}
 { % #1 is the name, #2 is the key-value set
  \seq_gput_right:Nn \g_runart_variables_seq { #1 }
  \prop_clear:N \l__runart_variables_temp_prop
  \keys_set:nn { runart/variables } { #2 }
  \prop_gclear_new:c { g_runart_var_#1_prop }
  \prop_gset_eq:cN { g_runart_var_#1_prop } \l__runart_variables_temp_prop
 }

% syntactic sugar
\cs_new_protected:Nn \__runart_gput:nn
 {
  \prop_gput:Nnn \l__runart_variables_temp_prop { #1 } { #2 }
 }

% keys
\keys_define:nn { runart/variables }
 {
  value .code:n = \__runart_gput:nn { value } { #1 },
  value .value_required:n = true,
  unit .code:n = \__runart_gput:nn { unit } { #1 },
  unit .value_required:n = true,
  name .code:n = \__runart_gput:nn { name } { #1 },
  name .value_required:n = true,
  sisetup .code:n = \__runart_gput:nn { sisetup } { #1 },
 }

\NewDocumentCommand{\matte}{m}
 { % #1 is the expression to output
  \tl_set:Nn \l__runart_variables_matte_tl { #1 }
  \seq_map_inline:Nn \g_runart_variables_seq
   {
    \regex_replace_all:nnN
     { ##1 }
     { \c{runart_variable_use_matte:n} \cB\{ ##1 \cE\} }
     \l__runart_variables_matte_tl
   }
  \tl_use:N \l__runart_variables_matte_tl
 }

\NewDocumentCommand{\formal}{m}
 {
  \tl_set:Nn \l__runart_variables_matte_tl { #1 }
  \seq_map_inline:Nn \g_runart_variables_seq
   {
    \regex_replace_all:nnN
     { ##1 }
     { \c{runart_variable_use_formal:n} \cB\{ ##1 \cE\} }
     \l__runart_variables_matte_tl
   }
  \tl_use:N \l__runart_variables_matte_tl
 }


\cs_new_protected:Nn \runart_variable_use_matte:n
 {
  \use:x % it's necessary to expand the optional argument
   {
    \SI
     [\prop_item:cn { g_runart_var_#1_prop } {sisetup}]
     {\prop_item:cn { g_runart_var_#1_prop } {value}}
     {\prop_item:cn { g_runart_var_#1_prop } {unit}}
   }
 }

\cs_new_protected:Nn \runart_variable_use_formal:n
 {
  \prop_item:cn { g_runart_var_#1_prop } { name }
 }
\ExplSyntaxOff
\begin{document}
{
\definevariable{a_car}
 {
  name=a_{\mathrm{car}},
  value=20,
  unit=\metre\per\second,
 }
\definevariable{v_car}
 {
  name=v_{\mathrm{car}},
  value=40,
  unit=\metre,
 }
\definevariable{t_car}
 {
  name=t_{\mathrm{car}},
  value=2,
  unit=\second,
  sisetup={color=red},
 }



\[
\matte{
  a_car=\frac{v_car}{t_car}
}
\]
}
Trying to redefine a variable:
\definevariable{t_car}
 {
  name=t_{\mathrm{car}},
  value=2,
  unit=\second,
  sisetup={color=red},
 }
\[
\matte{
  t_car
}
\]
\end{document}
Runar
  • 6,082
  • Please, note that the code has been slightly changed (there was a contradictory usage of variables); nothing that can influence your current problem, though. – egreg Jan 06 '16 at 10:55
  • I'm not sure what's the problem with global declaration; you can do \definevariable everywhere and this would clear the already assigned values, in case you use a previously used name. Note that it would be easy to define variables just in the scope of an equation environment, but not for align or gather. – egreg Jan 06 '16 at 11:11
  • I would need to print the variables both in text and in align, gather etc. Well, if it doesn't work it isn't the end of the world. Just means that I'll have to make sure all my variables are unique. – Runar Jan 06 '16 at 12:05

1 Answers1

2

Redefining variables should be possible; here's a polished up version, with a hint for error checking.

\documentclass{article}
\usepackage{xparse,l3regex,siunitx,xcolor}

\ExplSyntaxOn

\seq_new:N \g_runart_variables_seq
\prop_new:N \l__runart_variables_temp_prop
\tl_new:N \l__runart_variables_matte_tl
\tl_new:N \l__runart_variables_item_tl

\NewDocumentCommand{\definevariable}{mm}
 { % #1 is the name, #2 is the key-value set
  \seq_if_in:NnF \g_runart_variables_seq { #1 }
   {
    \seq_gput_right:Nn \g_runart_variables_seq { #1 }
   }
  \prop_clear:N \l__runart_variables_temp_prop
  \keys_set:nn { runart/variables } { #2 }
  \prop_gclear_new:c { g_runart_var_#1_prop }
  \prop_gset_eq:cN { g_runart_var_#1_prop } \l__runart_variables_temp_prop
 }

% syntactic sugar
\cs_new_protected:Nn \__runart_put:nn
 {
  \prop_put:Nnn \l__runart_variables_temp_prop { #1 } { #2 }
 }

% keys
\keys_define:nn { runart/variables }
 {
  value .code:n = \__runart_put:nn { value } { #1 },
  value .value_required:n = true,
  unit .code:n = \__runart_put:nn { unit } { #1 },
  unit .value_required:n = true,
  name .code:n = \__runart_put:nn { name } { #1 },
  name .value_required:n = true,
  sisetup .code:n = \__runart_put:nn { sisetup } { #1 },
 }

\NewDocumentCommand{\matte}{m}
 { % #1 is the expression to output
  \__runart_matte_or_formal:nn { runart_variable_use_matte:n } { #1 }
 }
\NewDocumentCommand{\formal}{m}
 {
  \__runart_matte_or_formal:nn { runart_variable_use_formal:n } { #1 }
 }

\cs_new_protected:Nn \__runart_matte_or_formal:nn
 {
  \tl_set:Nn \l__runart_variables_matte_tl { #2 }
  \seq_map_inline:Nn \g_runart_variables_seq
   {
    \regex_replace_all:nnN
     { ##1 }
     { \c{#1} \cB\{ ##1 \cE\} }
     \l__runart_variables_matte_tl
   }
  \tl_use:N \l__runart_variables_matte_tl
 }

\cs_new:Nn \__runart_use_prop:nn
 {
  \prop_if_in:cnTF { g_runart_var_#1_prop } { #2 }
   {
    \prop_item:cn { g_runart_var_#1_prop } { #2 }
   }
   {
    \str_case:nn { #2 }
     {
      {value}{999999}
      {unit}{\metre}
      {name}{undefined}
     }
   }
 }
\cs_generate_variant:Nn \tl_if_empty:nTF { f }

\cs_new_protected:Nn \runart_variable_use_matte:n
 {
  \use:x % it's necessary to expand the optional argument
   {
    \SI
     [\__runart_use_prop:nn { #1 } {sisetup}]
     {\__runart_use_prop:nn { #1 } {value}}
     {\__runart_use_prop:nn { #1 } {unit}}
   }
 }

\cs_new_protected:Nn \runart_variable_use_formal:n
 {
  \__runart_use_prop:nn { #1 } { name }
 }
\ExplSyntaxOff

\begin{document}

\definevariable{a_car}
 {
  name=a_{\mathrm{car}},
  value=20,
  unit=\metre\per\second,
 }
\definevariable{v_car}
 {
  name=v_{\mathrm{car}},
  value=40,
  unit=\metre,
 }
\definevariable{t_car}
 {
  name=t_{\mathrm{car}},
  value=2,
  unit=\second,
  sisetup={color=red},
 }
\[
\matte{
  a_car=\frac{v_car}{t_car}
}
\]
\[
\formal{
  a_car=\frac{v_car}{t_car}
}
\]
\[
\matte{
  a_car=\frac{v_car}{t_car}
}
\]
Trying to redefine a variable:
\definevariable{t_car}
 {
  name=t_{\mathrm{car}},
  value=2,
  unit=\second,
  sisetup={color=blue},
 }
\[
\matte{
  t_car
}
\]
Trying to redefine a variable, but forgetting the value:
\definevariable{t_car}
 {
  name=t_{\mathrm{car}},
  unit=\second,
  sisetup={color=blue},
 }
\[
\matte{
  t_car
}
\]
\end{document}

In case a value has been forgotten, it's possible to use a fallback one (and maybe to add a more sensible error message).

enter image description here

Using local assignments doesn't seem the best approach; for instance, you could use \definevariable in an equation environment (or variant thereof), but not in align, because each cell of an alignment forms a group.

egreg
  • 1,121,712
  • Thank you so much, you have really helped me out. When I get the time, I'll read up on l3regex and the like, so I don't have to bother you with these questions. It's just very different from LaTeX-syntax. – Runar Jan 06 '16 at 18:36