我有一个宏\DefineStrEqCaseMacro
,它定义了一个根据参数选择值的宏。有了这个,我试图取代手动方法:
\newcommand*{\SelectBasedOnValue}{beta}% Default Selector
\newcommand{\GreekSelector}[1][\SelectBasedOnValue]{%
\IfStrEqCase{#1}{%
{alpha}{\alpha}%
{beta}{\beta}%
{gamma}{\gamma}%
}[\chi]%
}%
%% Define this so we can step through the entire list of possible options
\csdef{GreekSelector List}{alpha, beta, gamma, <default>}%
和:
\DefineStrEqCaseMacro{GreekSelector}%
[\chi]% <-- Default value in case there is no match
{%
{alpha}{\alpha}%
{beta}{\beta}%
{gamma}{\gamma}%
}%
最后,我希望能够打印所有可能的值以及默认值(如果有)。以下代码产生所需的结果:
但是,您会注意到,在中\DefineStrEqCaseMacro
,我对可能的值列表进行了硬编码:
\IfValueTF{#2}{%
%% <> added to list ONLY if a default was specified}
\csdef{#1 List}{alpha, beta, gamma, <default>}%
}{%
\csdef{#1 List}{alpha, beta, gamma}%
}%
问题:
如何\csdef{}
根据#3
提供给此宏的内容自动定义它。此列表应包含全部每对预期的第一个选项\IfStrEqCase
。
概括:
给定以下形式的参数:
{alpha}{\alpha}%
{beta}{\beta}%
{gamma}{\gamma}%
对于任意数量的元素,我如何定义一个以逗号分隔的列表,以便我可以根据这些对中的第一个参数逐步遍历每个可能的匹配项。
代码:
\documentclass{article}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}% for \foreach
\newcommand*{\SelectBasedOnValue}{beta}
\NewDocumentCommand\DefineStrEqCaseMacro{% Simplified from actual use case
m% #1 = macro name to define
o% #2 = default value in case of no match
m% #3 = IfStrEq case parameters
}{%
%% https://tex.stackexchange.com/q/226987/4301
\expandafter\NewDocumentCommand\csname#1\endcsname{%
s% #1 = NOT used here
O{\SelectBasedOnValue}% #2 = selector
}{%
\IfValueTF{#2}{%
\IfStrEqCase{##2}{%
#3%
}[#2]%
}{%
\IfStrEqCase{##2}{%
#3%
}[\PackageError{\jobname}{Unknown selector "##2"}]%
}%
}%
\IfValueTF{#2}{% ----- ??? How create this list defined here???
%% <default> added to list ONLY if a default was specified}
\csdef{#1 List}{alpha, beta, gamma, <default>}%
}{%
\csdef{#1 List}{alpha, beta, gamma}%
}%
}%
%%% ---------------------------------------------------------- Manual Method
%%% This is the manual way of doing things, but is error prone
%\newcommand{\GreekSelector}[1][\SelectBasedOnValue]{%
% \IfStrEqCase{#1}{%
% {alpha}{\alpha}%
% {beta}{\beta}%
% {gamma}{\gamma}%
% }[\chi]%
%}%
Define this so we can step through the entire list of possible options
%\csdef{GreekSelector List}{alpha, beta, gamma, <default>}%
%%% ----------------------------------------------------------
%% ---------------------------------------------------------- Automated Method
\DefineStrEqCaseMacro{GreekSelector}%
[\chi]% <-- Default value in case there is no match
{%
{alpha}{\alpha}%
{beta}{\beta}%
{gamma}{\gamma}%
}%
%% ----------------------------------------------------------
\newcommand*{\TestGreekSelector}{%
Testing:
$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case
}%
\newcommand*{\ShowAllPossibleValues}[1]{%
\par
List of ALL options of \texttt{\string#1}:\par
\edef\MyList{\csuse{#1 List}}%
\foreach \x in \MyList {
{\ttfamily\bfseries \x}: $\csuse{#1}[\x]$\par
}
}%
\begin{document}
%\TestGreekSelector
\ShowAllPossibleValues{GreekSelector}%
\end{document}
答案1
像往常一样,我不会混淆xparse
、xstring
和etoolbox
,因为xparse
和expl3
已经提供了所需的工具。
\documentclass{article}
\usepackage{xparse}
\newcommand*{\SelectBasedOnValue}{beta}
\ExplSyntaxOn
% user level commands
\NewDocumentCommand{\defineselector}{mmm}
{
% #1 = command name to define, #2 = default, #3 = list to use
\grill_list_store:nnn { #1 } { #2 } { #3 }
\grill_list_define:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\displayselector}{m+m}
{% #1 = selector name, #2 = template
\grill_list_display:nn { #1 } { #2 }
}
% internal functions
\cs_new_protected:Nn \grill_list_store:nnn
{
% massage the list and store it
\seq_gclear_new:c { g__grill_list_#1_seq }
\seq_gset_from_clist:cn { g__grill_list_#1_seq } { #3 }
\tl_gclear_new:c { g__grill_list_#1_tl }
\tl_gset:cx { g__grill_list_#1_tl }
{
\seq_use:cn { g__grill_list_#1_seq } {}
}
% store the default value
\tl_gclear_new:c { g__grill_list_#1_default_tl }
\tl_gset:cn { g__grill_list_#1_default_tl } { #2 }
}
\cs_new_protected:Nn \grill_list_define:nnn
{
% define the selector
\exp_args:Nc \NewDocumentCommand {#1}{O{\SelectBasedOnValue}}
{
\str_case_e:nvF { ##1 } { g__grill_list_#1_tl } { #2 }
}
}
\cs_generate_variant:Nn \str_case_e:nnF {nv}
\cs_new_protected:Nn \grill_list_display:nn
{
% set the auxiliary function to the template
\cs_set_protected:Nn \__grill_list_show:nn { #2 }
\par
List ~ of ~ all ~ options ~ for ~ #1 \par
\seq_map_inline:cn { g__grill_list_#1_seq }
{
\__grill_list_show:nn ##1
}
\__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
}
\ExplSyntaxOff
\defineselector{GreekSelector}{\chi}{
{alpha}{\alpha},
{beta}{\beta},
{gamma}{\gamma}
}
\begin{document}
$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case
\displayselector{GreekSelector}{\texttt{#1}: $#2$\par}
\end{document}
的第二个参数\displayselector
是进行显示的模板,其中#1
和#2
代表每对中的第一个项目和第二个项目。
\displayselector
显示选择器的一种更通用的方法:将和的定义更改\grill_list_display:nn
为
\NewDocumentCommand{\displayselector}{m+m+m+m}
{% #1 = selector name, #2 = before, #3 = after, #4 = template
\grill_list_display:nnnn { #1 } { #2 } { #3 } { #4 }
}
\cs_new_protected:Nn \grill_list_display:nnnn
{
% trick to reuse the first argument
\cs_set_protected:Nn \__grill_list_before:n { #2 }
% set the auxiliary function to the template
\cs_set_protected:Nn \__grill_list_show:nn { #4 }
\__grill_list_before:n { #1 } % before
\seq_map_function:cN { g__grill_list_#1_seq } \__grill_list_unbrace:n
\__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
#3 % after
}
\cs_new:Nn \__grill_list_unbrace:n { \__grill_list_show:nn #1 }
现在你可以像这样调用显示
\displayselector{GreekSelector}
{List of options for #1\par
\begin{tabular}{@{}ll@{}}}
{\end{tabular}}
{\texttt{#1} & $#2$\\}
在第二个参数中,您可以放置应在显示之前的内容,并#1
引用第一个参数(选择器名称);在第三个参数中,您可以放置应在显示之后的内容。最后一个参数是每个条目的模板。
完整的代码:
\documentclass{article}
\usepackage{xparse}
\newcommand*{\SelectBasedOnValue}{beta}
\ExplSyntaxOn
% user level commands
\NewDocumentCommand{\defineselector}{mmm}
{
% #1 = command name to define, #2 = default, #3 = list to use
\grill_list_store:nnn { #1 } { #2 } { #3 }
\grill_list_define:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\displayselector}{m+m+m+m}
{% #1 = selector name, #2 = before, #3 = after, #4 = template
\grill_list_display:nnnn { #1 } { #2 } { #3 } { #4 }
}
% internal functions
\cs_new_protected:Nn \grill_list_store:nnn
{
% massage the list and store it
\seq_gclear_new:c { g__grill_list_#1_seq }
\seq_gset_from_clist:cn { g__grill_list_#1_seq } { #3 }
\tl_gclear_new:c { g__grill_list_#1_tl }
\tl_gset:cx { g__grill_list_#1_tl }
{
\seq_use:cn { g__grill_list_#1_seq } {}
}
% store the default value
\tl_gclear_new:c { g__grill_list_#1_default_tl }
\tl_gset:cn { g__grill_list_#1_default_tl } { #2 }
}
\cs_new_protected:Nn \grill_list_define:nnn
{
% define the selector
\exp_args:Nc \NewDocumentCommand {#1}{O{\SelectBasedOnValue}}
{
\str_case_e:nvF { ##1 } { g__grill_list_#1_tl } { #2 }
}
}
\cs_generate_variant:Nn \str_case_e:nnF {nv}
\cs_new_protected:Nn \grill_list_display:nnnn
{
% trick to reuse the first argument
\cs_set_protected:Nn \__grill_list_before:n { #2 }
% set the auxiliary function to the template
\cs_set_protected:Nn \__grill_list_show:nn { #4 }
\__grill_list_before:n { #1 } % before
\seq_map_function:cN { g__grill_list_#1_seq } \__grill_list_unbrace:n
\__grill_list_show:nn { <default> } { \tl_use:c { g__grill_list_#1_default_tl } }
#3 % after
}
\cs_new:Nn \__grill_list_unbrace:n { \__grill_list_show:nn #1 }
\ExplSyntaxOff
\defineselector{GreekSelector}{\chi}{
{alpha}{\alpha},
{beta}{\beta},
{gamma}{\gamma}
}
\begin{document}
$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case
\displayselector{GreekSelector}
{List of options for #1\par
\begin{tabular}{@{}ll@{}}}
{\end{tabular}}
{\texttt{#1} & $#2$\\}
\end{document}
一些解释
代码的主要部分是\grill_list_store:nnn
,其中对逗号分隔的对列表进行了处理。我将其制作成一个序列,用于显示,也用于制作平面列表,在示例中它将是
{alpha}{\alpha}{beta}{\beta}{gamma}{\gamma}
这是正确的格式\str_case_e:nnF
。此函数应该称为
\str_case_e:nnF { test string }
{
{case 1}{code}
{case 2}{code}
...
}
{ code for no match }
这里我使用了一个变体\str_case_e:nvF
,其中第三个参数是一个标记列表名称,正是之前构建的平面列表。
因此,我可以\GreekSelector
按照所示进行定义\grill_list_define:nnn
,传递所需的命令名称、不匹配时使用的默认值以及对的列表。
用于显示的宏有点棘手。在简化形式中,模板用于定义在中使用的辅助函数\seq_map_inline:cn
,其中参数用于选择经过处理序列。
答案2
我可以提供例程\RemoveEveryKthElement
,并在两个扩展步骤/两个链之后\UndelimitedListToCommaSpaceList
提供结果(通过扩展) 。\romannumeral
\expandafter
\RemoveEveryKthElement
确实从无分隔符(即括号嵌套)参数列表中删除每个第 K 个元素。
例如,您可以使用它来删除每个第二个元素。
\UndelimitedListToCommaSpaceList
将无分隔参数列表转换为一个列表,其中每个元素的周围括号都被删除,并且每个元素(最后一个元素除外)后面都附加了逗号和空格作为分隔符。
\documentclass{article}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{tikz}% for \foreach
%%<begin of copyrighted section>
\makeatletter
%%-----------------------------------------------------------------------------
%% Legal notes
%%.............................................................................
%% As portions of the compilable example which you are currently viewing
%% were delivered by Peter Grill, this work does not consist of the entire
%% compilable example you are currently viewing but it consists of everything
%% that within this example is nested between
%% a marker %%<begin of copyrighted section>
%% and a marker %%<end of copyrighted section>
%% regardless multiple/nested occurrences of such markers. ;-)
%%
%% Copyright (C) 2019 of this work by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%-----------------------------------------------------------------------------
%% Paraphernalia ;-) :
%%.............................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@exchange{\UD@firstoftwo}}{\UD@exchange{\UD@secondoftwo}}%
{\UD@exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%
%% !!! \UD@ExtractFirstArg's argument must not be empty. !!!
%% (You can test for emptiness via \UD@CheckWhetherNull
%% before applying \UD@ExtractFirstArg.)
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% Remove every K-th element from list of undelimited/brace-nested arguments,
%% beginning with the L-th element.
%%.............................................................................
%% \RemoveEveryKthElement{<Number K>}%
%% {<Number L of the element to begin with>}%
%% {<List of undelimited arguments>}%
%%
%% In case <Number K> is smaller than 1 yields: <List of undelimited arguments>
%% but with surplus space-tokens between elements removed.
%% (K actually is an ordinal number.
%% In the context of this macro it is unclear what is meant by non-positive
%% ordinal numbers. Thus it is unclear what elements are to be removed from
%% the list. Thus no elements are removed from the list. Only surplus space
%% tokens between elements are removed from the list in this case.)
%% Else:
%% In case <Number L of the element to begin with> is smaller than 1:
%% Error-Message. (The problem with non-positive ordinal numbers
%% again: A list of elements doesn't have a -1th or 0th
%% element. So it is unclear what should be the starting-point for
%% removing elements.)
%% Else: <List of undelimited arguments> but with the
%% L-th element and every K-th element following that element
%% removed.
%%
%% Caution:
%% !!! There is no error-checking for detecting whether the arguments
%% are numbers at all !!!
%%
%% Examples:
%%
%% \RemoveEveryKthElement{-1}{-4}{{1}{2}{3} {4}{5} {6} {7} {8} {9}{10}{11}{12}}
%% yields: {1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}
%%
%% \RemoveEveryKthElement{2}{-4}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields: Error-Message.
%%
%% \RemoveEveryKthElement{4}{4}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields: {1}{2}{3}{5}{6}{7}{9}{10}{11}
%%
%% \RemoveEveryKthElement{3}{6}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields: {1}{2}{3}{4}{5}{7}{8}{10}{11}
%%
%% \RemoveEveryKthElement{2}{1}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields: {2}{4}{6}{8}{10}{12}
%%
%% \RemoveEveryKthElement{2}{2}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields: {1}{3}{5}{7}{9}{11}
%%
%% Due to \romannumeral-expansion, the result is delivered after two
%% expansion-steps/\expandafter-chains
%%.............................................................................
\newcommand\RemoveEveryKthElement[3]{%
\romannumeral0%
\ifnum#1<1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@UndelimitedListRemoveSpacesloop{#3}{}}{%
\ifnum#2<1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{ \@latex@error{\string\RemoveEveryKthElement: Value of <Number L of the element to begin with> is too small}%
{Value of <Number L of the element to begin with> must be positive.}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@firstoftwo\expandafter{\expandafter}%
\romannumeral0\number\number#2 000%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@firstoftwo\expandafter{\expandafter}%
\romannumeral0\number\number#1 000%
}{%
\UD@RemoveEveryKthElementLoop{}%
}%
}{#3}{}%
}%
}%
}%
\newcommand\UD@UndelimitedListRemoveSpacesloop[2]{%
\UD@CheckWhetherNull{#1}{ #2}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@UndelimitedListRemoveSpacesloop
\expandafter{\UD@removespace#1}{#2}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@PassFirstToSecond
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\UD@ExtractFirstArg{#1}{#2}%
}{%
\expandafter\UD@UndelimitedListRemoveSpacesloop\expandafter{\UD@firstoftwo{}#1}%
}%
}%
}%
}%
\newcommand\UD@RemoveEveryKthElementLoop[5]{%
\UD@CheckWhetherNull{#4}{ #5}{%
\UD@CheckWhetherLeadingSpace{#4}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\UD@removespace#4}{\UD@RemoveEveryKthElementLoop{#1}{#2}{#3}%
}{#5}%
}{%
\UD@CheckWhetherNull{#3}{%
\UD@CheckWhetherNull{#1}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
\UD@RemoveEveryKthElementLoop{#2}{#2}{}%
}{#5}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@PassFirstToSecond
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\UD@ExtractFirstArg{#4}{#5}}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
\expandafter\UD@RemoveEveryKthElementLoop
\expandafter{\UD@firstoftwo{}#1}{#2}{}%
}%
}%
}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@PassFirstToSecond
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\UD@ExtractFirstArg{#4}{#5}}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#4}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
\UD@RemoveEveryKthElementLoop{}{#2}%
}%
}%
}%
}%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% Turn list of undelimited arguments into comma-and-space-list by stripping
%% surrounding braces from each element and attaching a comma and a space
%% With the last element no comma and no space will be attached.
%%.............................................................................
%%
%% \UD@UndelimitedListToCommaList{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}}
%% yields:
%% 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
%%
%% No warranties are given for what happens when the items within braces
%% contain commas ,too:
%%
%% \UD@UndelimitedListToCommaList{{1}{2}{3}{4}{5, A, B, C}{6}{7}{8}{9}{10}{11}{12}}
%% yields:
%% 1, 2, 3, 4, 5, A, B, C, 6, 7, 8, 9, 10, 11, 12
\newcommand\UndelimitedListToCommaSpaceList[1]{%
\romannumeral0\UD@UndelimitedListToCommaSpaceListloop{#1}{}{}{, }%
}
\newcommand\UD@UndelimitedListToCommaSpaceListloop[4]{%
\UD@CheckWhetherNull{#1}{ #2}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@UndelimitedListToCommaSpaceListloop
\expandafter{%
\UD@removespace#1}{#2}{#3}{#4}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@exchange
\romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
\UD@ExtractFirstArg{#1}{#2#3}%
}{%
\expandafter\UD@UndelimitedListToCommaSpaceListloop\expandafter{\UD@firstoftwo{}#1}%
}{#4}{#4}%
}%
}%
}%
%%-----------------------------------------------------------------------------
\makeatother
%%<end of copyrighted section>
\newcommand*{\SelectBasedOnValue}{beta}
\NewDocumentCommand\DefineStrEqCaseMacro{% Simplified from actual use case
m% #1 = macro name to define
o% #2 = default value in case of no match
m% #3 = IfStrEq case parameters
}{%
%% https://tex.stackexchange.com/q/226987/4301
\expandafter\NewDocumentCommand\csname#1\endcsname{%
s% #1 = NOT used here
O{\SelectBasedOnValue}% #2 = selector
}{%
\IfValueTF{#2}{%
\IfStrEqCase{##2}{%
#3%
}[#2]%
}{%
\IfStrEqCase{##2}{%
#3%
}[\PackageError{\jobname}{Unknown selector "##2"}]%
}%
}%
%%-- This is how one could probably create this list. ;-)
%% As you can define an option whose name is "<default>", I would
%% not add the default-value to the list but mention it separately.
%% Besides this one might specify emptiness as default.
%% Therefore I decided to have the macro #1default defined not in
%% terms of \long only when no value was specified.
%% For distinguising default being specified as emptiness from
%% default not being specified at all, compare the default-macro
%% to \empty.
%% Also I'd not use `\csdef` as that doesn't throw an error when
%% overriding a definition that already exists
\csname UD@PassFirstToSecond\expandafter\endcsname\expandafter{%
\romannumeral0%
\csuse{UD@exchange}{ }{%
\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
}%
\expandafter\UndelimitedListToCommaSpaceList\expandafter{%
\romannumeral0\csuse{UD@exchange}{ }{%
\expandafter\expandafter\expandafter
}%
\RemoveEveryKthElement{2}{2}{#3}%
}%
}%
{\expandafter\newcommand\csname#1 List\endcsname}%
\IfValueTF{#2}%
{\expandafter\newcommand\csname#1 default\endcsname{#2}}%
{\expandafter\newcommand\expandafter*\csname#1 default\endcsname{}}%
}%
%% ---------------------------------------------------------- Automated Method
\DefineStrEqCaseMacro{GreekSelector}%
[\chi]% <-- Default value in case there is no match
{%
{alpha}{\alpha}%
{beta}{\beta}%
{gamma}{\gamma}%
}%
%% ----------------------------------------------------------
\newcommand*{\TestGreekSelector}{%
Testing:
$\GreekSelector$, % <--- Select based on value of \SelectBasedOnValue
$\GreekSelector[alpha]$,
$\GreekSelector[Unknown]$.% <-- Select default case
}%
\newcommand*{\ShowAllPossibleValues}[1]{%
\par\noindent
List of ALL options of \texttt{\string#1}:%
\edef\MyList{\csuse{#1 List}}%
\foreach \x in \MyList {%
\par\noindent
{\ttfamily\bfseries \x}: $\csuse{#1}[\x]$%
}%
\par\noindent
\hrulefill\null
\par\noindent
\textit{$\langle$standard-option$\rangle$}:
{\ttfamily\bfseries\SelectBasedOnValue}: $\csuse{#1}$%
\ifcsequal{#1 default}{empty}{}{%
\par\noindent
\textit{$\langle$default-value$\rangle$}: $\csuse{#1 default}$%
}%
\smallskip
\par\noindent
{\footnotesize
(The \textit{$\langle$standard-option$\rangle$} is used when the
user did not specify any option.\\%
The \textit{$\langle$standard-option$\rangle$} can be changed via
redefining the macro \texttt{\string\SelectBasedOnValue}.%
\ifcsequal{#1 default}{empty}{}{%
\\The \textit{$\langle$default-value$\rangle$} is used when
the user did specify an unknown option.%
}%
)%
}\par
}%
\begin{document}
\TestGreekSelector
\bigskip
\ShowAllPossibleValues{GreekSelector}%
\end{document}