我想知道是否有任何巧妙的解决方法可以在自定义宏名称中包含数字?
我正在使用一个将 LaTeX 与计算机代数系统集成的平台,并且经常有一些变量a[1]
需要a[2]
分配给 tex 命令。
目前,我发现最实用的解决方案是使用罗马数字,例如\newcommand{\ai}{a[1]}
;但是,由于各种原因,它并不理想。
我试图想出某种方法可以使用参数来区分它们(例如\a{1}
,\a{2}
等等);然而,由于进一步的限制,我必须能够为不同的参数指定精确的输出。是否可以定义一个自定义宏,其输出对于不同的输入而不同?例如
\a{1} |--> foo
\a{2} |--> bar
\a{3} |--> baz
答案1
答案2
嗯,你不应该使用\a
已经定义的。但除此之外,命令自然可以根据参数执行不同的操作。
如果你知道你的参数是数字,那么你可以从序列中获取内容:
\documentclass{article}
\ExplSyntaxOn
\seq_new:N\l_rax_data_seq
\seq_set_from_clist:Nn \l_rax_data_seq {foo,bar,baz}
\NewDocumentCommand\rax{m}
{
\seq_item:Nn \l_rax_data_seq {#1}
}
\ExplSyntaxOff
\begin{document}
\rax{1}, \rax{2}, \rax{3}
\end{document}
答案3
虽然这种模仿listofitems
使用语法,但它允许除了简单列表之外的不同形式的输入,如果需要,它可以是非连续的。
\documentclass{article}
\makeatletter
\newcommand\setaside[1]{%
\def#1[##1]{%
\csname\expandafter\@gobble\string#1[##1]\endcsname}}
\def\define#1[#2]#3{%
\expandafter\def\csname\expandafter
\@gobble\string#1[#2]\endcsname{#3}}
\makeatother
\begin{document}
\setaside\a
\define\a[1]{foo}
\define\a[3]{baz}
\define\a[2]{bar}
\define\a[7]{barr}
Now, a[1] is \a[1] and a[3] is \a[3].
\end{document}
答案4
广告问题 1:
我想知道是否有任何巧妙的解决方法可以在自定义宏名称中包含数字?
我正在使用一个将 LaTeX 与计算机代数系统集成的平台,并且经常有一些变量
a[1]
需要a[2]
分配给 tex 命令。目前,我发现最实用的解决方案是使用罗马数字,例如
\newcommand{\ai}{a[1]}
;但是,由于各种原因,它并不理想。
因为在正常的类别代码制度下,您无法通过让 TeX 读取和标记 .tex-input 直接获取控制字标记\a1
、、\a2
等或\a[1]
、、等,并且正确调用与的组合有时看起来很麻烦,我提供了一个宏,可用于创建,例如,来自字符标记序列的控制字标记或来自字符标记序列的控制字标记等,或来自字符标记序列的控制字标记或来自字符标记序列的控制字标记等。\a[2]
\csname..\endcsname
\expandafter
\CsNameToCsToken
\a1
a1
\a2
a2
\a[1]
a[1]
\a[2]
a[2]
句法:
\CsNameToCsToken⟨stuff not in braces⟩{⟨NameOfCs⟩}
→
⟨stuff not in braces⟩\NameOfCs
(⟨stuff not in braces⟩
可能为空。)
(由于\romannumeral
-扩展,结果是通过触发两个扩展步骤获得的,例如,通过用 进行两次“命中” \expandafter
。)
使用这样的宏,您就不会受到特定定义命令的约束:
\CsNameToCsToken{foo}
→ \foo
。
\CsNameToCsToken\newcommand{foo}
→ \newcommand\foo
。
\CsNameToCsToken\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
。
\CsNameToCsToken\global\long\outer\def{foo}
→ \global\long\outer\def\foo
。
\CsNameToCsToken\expandafter{foo}\bar
→ \expandafter\foo\bar
。
\CsNameToCsToken\let{foo}=\bar
→ \let\foo=\bar
。
\CsNameToCsToken\CsNameToCsToken\let{foo}={bar}
→ \CsNameToCsToken\let\foo={bar}
→ \let\foo=\bar
。
\CsNameToCsToken\string{foo}
→ \string\foo
。
\CsNameToCsToken\meaning{foo}
→ \meaning\foo
。
\CsNameToCsToken\NewDocumentCommand{foo}...
→ \NewDocumentCommand\foo...
。
\makeatletter
%%===============================================================================
%% End \romannumeral-driven expansion safely:
%%===============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%===============================================================================
%% Obtain control sequence token from name of control sequence token:
%%===============================================================================
%% \CsNameToCsToken<stuff not in braces>{NameOfCs}
%% -> <stuff not in braces>\NameOfCs
%% (<stuff not in braces> may be empty.)
\@ifdefinable\CsNameToCsToken{%
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{\UD@stopromannumeral#1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
\documentclass{article}
\begin{document}
\begin{verbatim}
Define \A[1]:
\CsNameToCsToken\newcommand*{A[1]}{%
This is the replacement-text of the command whose name is \texttt{A[1]}.%
}
\end{verbatim}
\CsNameToCsToken\newcommand*{A[1]}{%
This is the replacement-text of the command whose name is \texttt{A[1]}.%
}
\begin{verbatim}
Define \A[2]:
\CsNameToCsToken\newcommand*{A[2]}{%
This is the replacement-text of the command whose name is \texttt{A[2]}.%
}
\end{verbatim}
\CsNameToCsToken\newcommand*{A[2]}{%
This is the replacement-text of the command whose name is \texttt{A[2]}.%
}
\begin{verbatim}
Using \A[1]: \CsNameToCsToken{A[1]}
\end{verbatim}
\CsNameToCsToken{A[1]}
\begin{verbatim}
Using \A[2]: \CsNameToCsToken{A[2]}
\end{verbatim}
\CsNameToCsToken{A[2]}
\end{document}
广告问题2:
是否可以定义一个自定义宏,其输出对于不同的输入而不同?
根据构成宏参数的标记,可以有更多可能性来实现不同的输出/分叉。我列出了其中的六个。
可能性 1:例如,参见\ref
-command。其输出取决于输入/参数。使用该包,zref
您可以定义自己的引用标签类,用于为命名常量分配值,以及自己的引用命令类,用于检索这些值。优点:在使用 LaTeX 的第二次编译中,所有变量的值在整个编译过程中都可用,而不仅仅是从定义它们的那一刻开始。缺点:您至少需要两次 LaTeX 运行才能匹配。
\errorcontextlines=10000
\RequirePackage{xparse}
\documentclass{article}
\usepackage{zref}
\makeatletter
\@ifdefinable\@zrefpropdefined{%
\ZREF@Robust\def\@zrefpropdefined{%
\zref@wrapper@babel\@ZREFpropdefined
}%
}%
\@ifdefinable\@ZREFpropdefined{%
\def\@ZREFpropdefined#1#2#3#4{%
% #1 - label
% #2 - property
% #3 - tokens in case label and property are available
% #4 - tokens in case label undefined or property not available
\zref@ifrefundefined{#1}{%
\zref@refused{#1}#4%
}{%
\zref@ifrefcontainsprop{#1}{#2}{#3}{%
\protect\G@refundefinedtrue
\@latex@warning{%
Missing property `#2' in reference `#1' on page \thepage
}%
#4%
}%
}%
}%
}%
\zref@newprop{ValueOfVariable}{\nfss@text{\reset@font\bfseries??}}%
\NewDocumentCommand\IntroduceNewVariable{mm}{%
\@bsphack
\zref@wrapper@immediate{%
\zref@setcurrent{ValueOfVariable}{#2}%
\zref@labelbyprops{#1}{ValueOfVariable}%
}%
\@esphack
}%
\NewDocumentCommand\RetrieveValueOfVariable{m}{%
\zref@extractdefault{#1}{ValueOfVariable}{%
\@zrefpropdefined{#1}{ValueOfVariable}{%
\protect\G@refundefinedtrue
\@latex@warning{%
Unspecified problem/error related to reference `#1' on page \thepage
}%
}{}%
\nfss@text{\reset@font\bfseries??}%
}%
}%
\makeatother
\begin{document}
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}
\IntroduceNewVariable{A[1]}{Value of variable \texttt{A[1]}.}%
Some Text
\IntroduceNewVariable{A[2]}{Value of variable \texttt{A[2]}.}%
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}
\end{document}
可能性 2:如果你不介意处理 pgfkeys 包(它是 TikZ/pgf 的一个组件)的奇怪之处,你可以实现一个用于解析逗号列表的接口,其中
- 没有的项目
=
表示要检索其值的变量的名称,并且 - 带有 的项目表示分配一个变量,该变量的名称位于由 右侧的标记组成的值
=
左侧。=
=
PutTokens
是一个特殊键:它既不表示变量赋值,也不表示变量检索。构成其值的标记只是放入标记队列中。
请注意,无论键/值是否在括号之间,pgfkeys 都会删除键和/或值周围的空格。(可能需要使用括号来屏蔽逗号,这些逗号不应分隔逗号列表的元素,但应是值或键的名称的一部分。可能需要使用括号来屏蔽逗号,=
这些逗号不应分隔键和值,但应是值或键的名称的一部分。)
%\errorcontextlines=10000
\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\newcommand\ValueOfUndefinedVariable[1]{%
\GenericError{\space\@spaces\@spaces}{%
Error: Variable `#1` undefined\on@line
}{}{%
\string\Variable{<variable's name>=<tokens denoting variable's value>}%
\MessageBreak defines variables.%
}%
\nfss@text{\reset@font\bfseries??}%
}%
\@ifdefinable\@gobbletopgfkeysnovalue{%
\long\def\@gobbletopgfkeysnovalue#1\pgfkeysnovalue{}%
}%
\@ifdefinable\@keeptopgfkeysnovalue{%
\long\def\@keeptopgfkeysnovalue#1\pgfkeysnovalue{#1}%
}%
\newcommand\CheckWhetherPgfkeysnovalue[1]{%
\ifcat$\detokenize\expandafter{\@gobbletopgfkeysnovalue#1\pgfkeysnovalue}$%
\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
{%
\ifcat$\detokenize\expandafter\expandafter\expandafter{%
\@keeptopgfkeysnovalue\@firstofone{}#1%
}$%
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}{\@secondoftwo}%
}%
\makeatother
\newcommand\MyVariablesHandle[2]{%
% #1 name of variable
% #2 value of variable
\CheckWhetherPgfkeysnovalue{#2}{%
\pgfkeys{/MyVariables/#1/.initial={\ValueOfUndefinedVariable{#1}},
/MyVariables/#1%
}%
}{%
\pgfkeys{/MyVariables/#1/.initial={#2}}%
}%
}%
\pgfkeys{%
/MyVariables/.unknown/.code={%
\expandafter\MyVariablesHandle\expandafter{\pgfkeyscurrentname}{#1}%
},
/MyVariables/PutTokens/.code={#1},
/MyVariables/PutTokens/.value required,
}%
\newcommand\Variables[1]{\pgfkeys{/MyVariables/.cd,#1}}
\begin{document}
% Define variables A[1] and A[2]
\Variables{%
A[1]={Value of variable \texttt{A[1]}},
A[2]={Value of variable \texttt{A[2]}},
}%
\Variables{%
% Deliver values of variables A[1] and A[2]
PutTokens={\noindent Retrieving the value of Variable \texttt{A[1]} yields:\space },
A[1],
PutTokens={.\\Retrieving the value of Variable \texttt{A[2]} yields:\space },
A[2],
% Define variables A[3] and A[4]
A[3]={Value of variable \texttt{A[3]}},
A[4]={Value of variable \texttt{A[4]}},
% Deliver values of A[3] and A[4]
PutTokens={.\\Retrieving the value of Variable \texttt{A[3]} yields:\space },
A[3],
PutTokens={.\\Retrieving the value of Variable \texttt{A[4]} yields:\space },
A[4],
PutTokens={.},
}%
\bigskip
% Deliver values of variables A[1] and A[2] and [A3] and [A4]
\Variables{%
% Print variables A[1] and A[2] and [A3] and [A4]
PutTokens={\noindent Retrieving the value of Variable \texttt{A[1]} yields:\space },
A[1],
PutTokens={.\\Retrieving the value of Variable \texttt{A[2]} yields:\space },
A[2],
PutTokens={.\\Retrieving the value of Variable \texttt{A[3]} yields:\space },
A[3],
PutTokens={.\\Retrieving the value of Variable \texttt{A[4]} yields:\space },
A[4],
PutTokens={.},%
}%
\bigskip
% Deliver values of variables A[1] and A[2] and [A3] and [A4]
\noindent Retrieving the value of Variable \texttt{A[1]} yields: \Variables{A[1]}.
\\Retrieving the value of Variable \texttt{A[2]} yields: \Variables{A[2]}.
\\Retrieving the value of Variable \texttt{A[3]} yields: \Variables{A[3]}.
\\Retrieving the value of Variable \texttt{A[4]} yields: \Variables{A[4]}.
\end{document}
可能性 3:您说您使用一个将 LaTeX 与计算机代数系统相集成的平台。
我不知道计算机代数系统如何将变量传递给 LaTeX。
使用 .csv 文件和包 datatool 的方法可能很有趣。
使用以下方法数据库文件变量.csv被建造。
从那里通过创建一个数据库\DTLloaddb
。
每次要检索变量的值时都会迭代该数据库。 这是相当低效的。
它依赖于NameOfVariable
形成数据库的主键。
\errorcontextlines=10000
%=====Let LaTeX create a .csv-file holding a database of names and ========
% values of named constants.
% This could as well be done via export to .csv-file by whatsoever
% other program.
\begin{filecontents*}{variables.csv}
PrimaryKey,NameOfVariable,ValueOfVariable
1,A[1],Value of variable \texttt{A[1]}.
2,A[2],Value of variable \texttt{A[2]}.
3,A[3],Value of variable \texttt{A[3]}.
4,A[4],Value of variable \texttt{A[4]}.
\end{filecontents*}
%==========================================================================
\documentclass{article}
\usepackage{datatool}% For loading and iterating databases; internally loads the package ifthen
\makeatletter
\newcommand\ValueOfUndefinedVariable[1]{%
\GenericError{\space\@spaces\@spaces}{%
Error: Variable `#1` undefined\on@line
}{}{%
\string\Variable{<variable's name>=<tokens denoting variable's value>}%
\MessageBreak defines variables.%
}%
\nfss@text{\reset@font\bfseries??}%
}%
\newif\ifvariabledefined
\newcommand\RetrieveValueOfVariable[2]{%
\global\variabledefinedfalse
\DTLforeach*[{\equal{#1}{\NameOfVariable}}]{variables}{%
\PrimaryKey=PrimaryKey,%
\NameOfVariable=NameOfVariable,%
\ValueOfVariable=ValueOfVariable%
}{%
\dtlbreak
\global\variabledefinedtrue
\ValueOfVariable
}%
\ifvariabledefined\expandafter\@gobble\else\expandafter\@firstofone\fi
{#2}%
}%
\makeatother
\DTLloaddb{variables}{variables.csv}
\begin{document}
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}{\ValueOfUndefinedVariable{A[1]}}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}{\ValueOfUndefinedVariable{A[2]}}
Retrieving the value of Variable \texttt{A[3]} yields:
\RetrieveValueOfVariable{A[3]}{\ValueOfUndefinedVariable{A[3]}}
Retrieving the value of Variable \texttt{A[4]} yields:
\RetrieveValueOfVariable{A[4]}{\ValueOfUndefinedVariable{A[4]}}
\end{document}
当然,你可以结合使用这个功能\CsnameToCsToken
来为每个变量定义一个宏。
这样,你不必每次检索变量的值时都迭代数据库。
\errorcontextlines=10000
%=====Let LaTeX create a .csv-file holding a database of names and ========
% values of named constants.
% This could as well be done via export to .csv-file by whatsoever
% other program.
\begin{filecontents*}{variables.csv}
PrimaryKey,NameOfVariable,ValueOfVariable
1,A[1],Value of variable \texttt{A[1]}.
2,A[2],Value of variable \texttt{A[2]}.
3,A[3],Value of variable \texttt{A[3]}.
4,A[4],Value of variable \texttt{A[4]}.
\end{filecontents*}
%==========================================================================
\documentclass{article}
\usepackage{datatool}% For loading and iterating databases; internally loads
% the package ifthen
\makeatletter
%%===============================================================================
%% End \romannumeral-driven expansion safely:
%%===============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%===============================================================================
%% Obtain control sequence token from name of control sequence token:
%%===============================================================================
%% \CsNameToCsToken<stuff not in braces>{NameOfCs}
%% -> <stuff not in braces>\NameOfCs
%% (<stuff not in braces> may be empty.)
\@ifdefinable\CsNameToCsToken{%
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{\UD@stopromannumeral#1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
%%===============================================================================
\newcommand\ValueOfUndefinedVariable[1]{%
\GenericError{\space\@spaces\@spaces}{%
Error: Variable `#1` undefined\on@line
}{}{%
\string\Variable{<variable's name>=<tokens denoting variable's value>}%
\MessageBreak defines variables.%
}%
\nfss@text{\reset@font\bfseries??}%
}%
%%===============================================================================
\newcommand\RetrieveValueOfVariable[2]{%
\@ifundefined{Variable@#1}{#2}{\CsNameToCsToken{Variable@#1}}%
}%
\makeatother
\DTLloaddb{variables}{variables.csv}
\DTLforeach*{variables}{%
\PrimaryKey=PrimaryKey,%
\NameOfVariable=NameOfVariable,%
\ValueOfVariable=ValueOfVariable%
}{%
\csname UD@exchange\expandafter\endcsname\expandafter{\expandafter{\ValueOfVariable}}{%
\CsNameToCsToken\newcommand*{Variable@\NameOfVariable}%
}%
\CsNameToCsToken\CsNameToCsToken\global\let{Variable@\NameOfVariable}{Variable@\NameOfVariable}%
}%
\begin{document}
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}{\ValueOfUndefinedVariable{A[1]}}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}{\ValueOfUndefinedVariable{A[2]}}
Retrieving the value of Variable \texttt{A[3]} yields:
\RetrieveValueOfVariable{A[3]}{\ValueOfUndefinedVariable{A[3]}}
Retrieving the value of Variable \texttt{A[4]} yields:
\RetrieveValueOfVariable{A[4]}{\ValueOfUndefinedVariable{A[4]}}
\end{document}
可能性 4:您可以使用 expl3-property-list—属性是变量,属性的值是变量的值:
\errorcontextlines=10000
\documentclass{article}
\RequirePackage{xparse}
\ExplSyntaxOn
\prop_new:c {Variables}
\NewDocumentCommand\RetrieveValueOfVariable{mm}{
% #1 = Name of variable
% #2 = tokens in case variable is not defined
\prop_if_in:cnTF {Variables} {#1}
{ \prop_item:cn {Variables} {#1} }
{ #2 }
}
\NewDocumentCommand\SetVariables{m}{
\keyval_parse:NNn \__MYSetvariable:n \__MYSetvariable:nn {#1}
}
\cs_new:Nn \__MYSetvariable:n {\__MYSetvariable:nn {#1}{}}
\cs_new:Nn \__MYSetvariable:nn {
\prop_put:cnn {Variables} {#1} {#2}
}
\ExplSyntaxOff
\makeatletter
\NewDocumentCommand\ValueOfUndefinedVariable{}{\nfss@text{\reset@font\bfseries??}}%
\makeatother
\SetVariables{%
A[1]={Value of variable \texttt{A[1]}.},
A[2]={Value of variable \texttt{A[2]}.},
}%
\SetVariables{%
A[3]={Value of variable \texttt{A[3]}.},
A[4]={Value of variable \texttt{A[4]}.},
}%
\begin{document}
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[3]} yields:
\RetrieveValueOfVariable{A[3]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[4]} yields:
\RetrieveValueOfVariable{A[4]}{\ValueOfUndefinedVariable}
\end{document}
可能性 5:使用分隔参数进行分叉。这很麻烦,用途有限,但很有趣,因为您在用于检索值的两个宏的定义文本中设置变量的值:
\errorcontextlines=10000
\documentclass{article}
\makeatletter
\newcommand\ValueOfUndefinedVariable{\nfss@text{\reset@font\bfseries??}}%
\@ifdefinable\gobbletoexclam{\long\def\gobbletoexclam#1!{}}%
\newcommand\RetrieveValueOfVariable[2]{%
\ifcat$\detokenize\expandafter{\gobbletoexclam#1!}$%
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\VariableSelectionFork
!#1!A[2]!A[3]!A[4]!{Value of variable \texttt{A[1]}.}%
!A[1]!#1!A[3]!A[4]!{Value of variable \texttt{A[2]}.}%
!A[1]!A[2]!#1!A[4]!{Value of variable \texttt{A[3]}.}%
!A[1]!A[2]!A[3]!#1!{Value of variable \texttt{A[4]}.}%
!A[1]!A[2]!A[3]!A[4]!{#2}!!!!%
}{#2}%
}%
\@ifdefinable\VariableSelectionFork{%
\long\def\VariableSelectionFork#1!A[1]!A[2]!A[3]!A[4]!#2#3!!!!{#2}%
}%
\makeatother
\begin{document}
Retrieving the value of Variable \texttt{A[1]} yields:
\RetrieveValueOfVariable{A[1]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[2]} yields:
\RetrieveValueOfVariable{A[2]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[3]} yields:
\RetrieveValueOfVariable{A[3]}{\ValueOfUndefinedVariable}
Retrieving the value of Variable \texttt{A[4]} yields:
\RetrieveValueOfVariable{A[4]}{\ValueOfUndefinedVariable}
\end{document}
可能性 6:如果变量的名称表示连续的编号,则另一种方法可能是从参数列表中提取第 k 个参数,该列表的元素表示变量的值:
\errorcontextlines=10000
\documentclass{article}
\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%% {<list of undelimited args>} %
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%% \ExtractKthArg{3}{not available}{ABCDE} yields: C
%%
%% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD
%%
%% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available
%%
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
\romannumeral%
% #1: <integer number K>
% #2: <action if there is no K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
\endgroup
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
\UD@CheckWhetherNull{##1}{%
\UD@ExtractFirstArgLoop{##3#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle
%% pointed out in <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%=============================================================================
%% End of code for \ExtractKthArg.
\newcommand\ValueOfUndefinedVariable{\nfss@text{\reset@font\bfseries??}}%
\newcommand\RetrieveVariableIndex[3]{%
\@ifundefined{Variables#1}{#3}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{\csname Variables#1\endcsname}{%
\ExtractKthArg{#2}{#3}%
}%
}%
}%
\makeatother
\newcommand\VariablesA{%
{Value of variable \texttt{A$_1$}.}%
{Value of variable \texttt{A$_2$}.}%
{Value of variable \texttt{A$_3$}.}%
{Value of variable \texttt{A$_4$}.}%
}%
\newcommand\VariablesB{%
{Value of variable \texttt{B$_1$}.}%
{Value of variable \texttt{B$_2$}.}%
{Value of variable \texttt{B$_3$}.}%
{Value of variable \texttt{B$_4$}.}%
}%
\begin{document}
Extacting the value of variable \texttt{A$_1$} yields: \RetrieveVariableIndex{A}{1}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{A$_2$} yields: \RetrieveVariableIndex{A}{2}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{A$_3$} yields: \RetrieveVariableIndex{A}{3}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{A$_4$} yields: \RetrieveVariableIndex{A}{4}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{B$_1$} yields: \RetrieveVariableIndex{B}{1}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{B$_2$} yields: \RetrieveVariableIndex{B}{2}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{B$_3$} yields: \RetrieveVariableIndex{B}{3}{\ValueOfUndefinedVariable}
Extacting the value of variable \texttt{B$_4$} yields: \RetrieveVariableIndex{B}{4}{\ValueOfUndefinedVariable}
\end{document}