使用 \IfStrEqCase 列表创建 CSV 文件

使用 \IfStrEqCase 列表创建 CSV 文件

我有一个宏\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

像往常一样,我不会混淆xparsexstringetoolbox,因为xparseexpl3已经提供了所需的工具。

\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}

在此处输入图片描述

相关内容