2

I would like to be able to perform an "indexed" assign by concatenating two (or more) portions of a command name, both for creating a new command and for referencing an existing command. Here is an example that shows that I need. Non-working, of course.

\documentclass[10pt]{article}

\usepackage{mathtools}
\usepackage{fontspec}
\usepackage{unicode-math}
\usepackage{lualatex-math}

\setmainfont{STIX Two Text}
\setmathfont{STIX Two Math}

\usepackage{logix} % Load logix font for additional symbols.

\newCommand oldabc  123               % Existing value.

\newcommand indexedSet[1] {           % Macro to do an indexed assign and an indexed reference.
   \newcommand  val##1 {old##1}
   }

\indexedSet{abc}                      % Defines valabc as 123

\begin{document}

   \end{document}
  • Maybe this helps: tex.stackexchange.com/q/39380/121799 – user121799 Jun 4 at 23:56
  • \documentclass[10pt]{article} \newcommand\oldabc{123} \newcommand\indexedSet[1]{\expandafter\def\csname val#1\endcsname{\csname old#1\endcsname}} \begin{document} \indexedSet{abc} \valabc \end{document} – user121799 Jun 5 at 0:02
  • Thank you. That worked. – Michael Lee Finney Jun 5 at 0:38
2

It is partly a matter of opinion, but the easiest way to do this is to use the \csdef and \csuse commands from the etoolbox package. At least using these commands is easier for me to read as you can define \IndexedSet as

\newcommand\indexedSet[1]{\csdef{val#1}{\csuse{old#1}}}

Depending on what you put into these commands and how often you change them you might to actually use

\newcommand\indexedSet[1]{\csxdef{val#1}{\csuse{old#1}}}

as this will force the "old" command to expand to its current value. Finally, you should also check to make sure that the old command is actually defined, which you can do using \ifcsdef:

\newcommand\indexedSet[1]{\csxdef{val#1}{\ifcsdef{old#1}{\csuse{old#1}}{???}}}

So, if the "old" command is not defined then the new command is set to ???.

Here is a full MWE:

\documentclass[10pt]{article}

\usepackage{etoolbox}
\newcommand\oldabc{123} % initial value.

% Macro to do an indexed assign and an indexed reference.
\newcommand\indexedSet[1]{\csxdef{val#1}{\ifcsdef{old#1}{\csuse{old#1}}{???}}}

\begin{document}
   abc: \indexedSet{abc} \valabc

   pqf: \indexedSet{pqf} \valpqf
\end{document}

that produces the expected output:

enter image description here

  • Thank you. That looks like a good solution as well. It doesn't seem like it saves much over wrapping \csname and \encsaname around the parts of the name. I have already incorporated the suggestion by marmot which has saved me over a 1000 lines in my package. Adding looping (not that I know how to do that in TeX yet) would probably save me another 100 or 150 lines once I am done. That is a comparatively minor optimization and I will look at that once this change is complete. Probably later tonight. – Michael Lee Finney Jun 5 at 2:13
  • @MichaelLeeFinney Yes that's exactly right: this just save your from writing \csname...\]endcsname everywhere - and, I think, it is easier to read. There are various different ways of looping and without see your code it is difficult to say what will work best for you. For looping over comma separated lists look at \docsvlist in the etoolbox package. For "standard" for-loops have a look at \foreach in pgffor -- or delve into the wonders of LaTeX3 package – Andrew Jun 5 at 6:09
1

Thanks to a suggest by marmot, the following works.

\documentclass[10pt]{article}

\usepackage{mathtools}
\usepackage{fontspec}
\usepackage{unicode-math}
\usepackage{lualatex-math}

\setmainfont{STIX Two Text}
\setmathfont{STIX Two Math}

\usepackage{logix} % Load logix font for additional symbols.

\newcommand \oldabc  {123}            % Existing value.

\newcommand\indexedSet[1]  {
   \expandafter\def\csname val#1\endcsname{\csname old#1\endcsname} }

\indexedSet{abc}                      % Defines valabc as 123

\begin{document}

   \valabc

   \end{document}
  • Expanding \valabc does not yield 123 but does yield \csname oldabc\endcsname. Perhaps \newcommand\indexedSet[1]{\expandafter\def\csname val#1\expandafter\expandafter\expandafter\endcsname\expandafter\expandafter\expandafter{\csname old#1\endcsname}} – Ulrich Diez Jun 5 at 13:59
1

Just use \csname with appropriate expansions.

\documentclass{article}

\newcommand{\indexedSet}[1]{%
  \expandafter\let\csname val#1\expandafter\endcsname\csname old#1\endcsname
}

\newcommand\oldabc{123}

\begin{document}

\indexedSet{abc}

\valabc

\end{document}

With etoolbox you can shorten the definition:

\usepackage{etoolbox}

\newcommand{\indexedSet}[1]{\csletcs{val#1}{old#1}}
0
\documentclass[10pt]{article}

%----------------------------------------------------------------------
% \name <tokens not nested in braces>{macroname}
%  ->  <tokens not nested in braces>\macroname
% Examples:
%   \name{foo} -> \foo
%   \name\string{foo} -> \string\foo
%   \name\meaning{foo} -> \meaning\foo
%   \name\newcommand*{foo}... -> \newcommand*\foo...
%   \name\name\let{foo}={bar} -> \name\let\foo={bar} -> \let\foo=\bar
%......................................................................
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{%
  \expandafter\exchange\expandafter{\csname #2\endcsname}{ #1}%
}%
\newcommand\exchange[2]{#2#1}%


\usepackage{mathtools}
%\usepackage{fontspec}
%\usepackage{unicode-math}
%\usepackage{lualatex-math}

%\setmainfont{STIX Two Text}
%\setmathfont{STIX Two Math}

%\usepackage{logix} % Load logix font for additional symbols.

\newcommand\oldabc{123}               % Existing value.

\newcommand\indexedSet[1]{%           % Macro to do an indexed assign and an indexed reference.
  \name\name{@ifdefinable}{val#1}{%
    \name\name\let{val#1}={old#1}%
  }%
}%

\indexedSet{abc}                      % Defines valabc as 123

\begin{document}

\texttt{\string\oldabc: \meaning\oldabc}

\texttt{\string\valabc: \meaning\valabc}

\texttt{\name\string{valabc}: \name\meaning{valabc}}

\end{document}

enter image description here

Adding some raising of error-messages in case \valabc is already defined or \oldabc is undefined, could look like this:

\documentclass[10pt]{article}

%----------------------------------------------------------------------
% \name <tokens not nested in braces>{macroname}
%  ->  <tokens not nested in braces>\macroname
% Examples:
%   \name{foo} -> \foo
%   \name\string{foo} -> \string\foo
%   \name\meaning{foo} -> \meaning\foo
%   \name\newcommand*{foo}... -> \newcommand*\foo...
%   \name\name\let{foo}={bar} -> \name\let\foo={bar} -> \let\foo=\bar
%......................................................................
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\exchange[2]{#2#1}%
%----------------------------------------------------------------------
% \PreambleMacroError{\macro}{<error text>}{<immediate-help-text>}
%......................................................................
\begingroup\makeatletter\@firstofone{%
  \endgroup
  \newcommand*\PreambleMacroError[3]{%
    \GenericError{%
      \space\space\space\@spaces\@spaces\@spaces
    }{%
      LaTeX Error: Inapproriate usage of macro \string#1\on@line.\MessageBreak
      (\string#1 is defined in the document's preamble.)\MessageBreak
      Problem: #2%
    }{%
      Have a look at the comments in the preamble of this document.%
    }{#3}%
  }%
}%
%----------------------------------------------------------------------


\usepackage{mathtools}
%\usepackage{fontspec}
%\usepackage{unicode-math}
%\usepackage{lualatex-math}

%\setmainfont{STIX Two Text}
%\setmathfont{STIX Two Math}

%\usepackage{logix} % Load logix font for additional symbols.

\newcommand\oldabc{123}               % Existing value.

\newcommand\indexedSet[1]{%           % Macro to do an indexed assign and an indexed reference.
  \name{@ifundefined}{old#1}{%
    \PreambleMacroError{\indexedSet}%
                       {Macro \name\string{old#1} undefined}%
                       {Setting \name\string{val#1} to \name\string{old#1} 
                        does only make sense when\MessageBreak
                        \name\string{old#1} is defined.}%
    \name{@ifundefined}{val#1}{}{%
      \PreambleMacroError{\indexedSet}%
                         {Macro \name\string{val#1} already defined}%
                         {Setting \name\string{val#1} to \name\string{old#1} should not
                          override an existing definition of\MessageBreak
                          \name\string{val#1}.}%
    }%
  }{%
    \name{@ifundefined}{val#1}{%
      \name\name\let{val#1}={old#1}%
    }{%
      \PreambleMacroError{\indexedSet}%
                         {Macro \name\string{val#1} already defined}%
                         {Setting \name\string{val#1} to \name\string{old#1} should not
                          override an existing definition of\MessageBreak
                          \name\string{val#1}.}%
    }%
  }%
}%

\indexedSet{abc}                      % Defines valabc as 123

\begin{document}

\texttt{\string\oldabc: \meaning\oldabc}

\texttt{\string\valabc: \meaning\valabc}

\texttt{\name\string{valabc}: \name\meaning{valabc}}

\end{document}

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.