如何检查宏是否扩展为多个字符串之一?

如何检查宏是否扩展为多个字符串之一?

我有以下问题:我正在使用该包etoolbox检查宏(例如\foo)是否扩展为几个可能的字符串之一(例如,,,bar或其他)。我需要根据字符串扩展为哪个字符串执行不同的代码。bazabc\foo

我当前的解决方案是

\ifdefstring{\foo}{bar}
{% if \foo is bar
  <do something>
}
{% if \foo is not bar
  \ifdefstring{\foo}{baz}
  {% if \foo is baz
    <do something else>
  }
  {% if \foo is not bar or baz
    \ifdefstring{\foo}{abc}
    {% if \foo is abc
      <do something else>
    }
    {% if \foo is not bar, baz, or abc
      <do something else>
    }
  }
}

虽然这种方法可行,但由于嵌套深度太深,因此并不令人满意,并且不能很好地推广到较长的可能字符串列表。在我的实际情况下,我最多需要检查八个可能的字符串,因此此解决方案会产生难以阅读的代码。

我的问题是,如果宏扩展为多个字符串之一,我有哪些替代选项(在 LaTeX2e 中)可以检查?

理想情况下,该解决方案也适用于其他etoolbox比较命令(例如\ifstrequal)。但是,该解决方案不需要使用该etoolbox包。

答案1

如果\foo扩展为一个简单的字符串,它在中存在\csname,则可以使用以下方法,不需要 LaTeX3:

\documentclass{article}

\makeatletter
\@namedef{foo@bar}{%
  \typeout{\noexpand\foo is bar}%
  % <do something>
}
\@namedef{foo@baz}{%
  \typeout{\noexpand\foo is baz}%
  % <do something>
}
\@namedef{foo@abc}{%
  \typeout{\noexpand\foo is abc}%
  % <do someting>
}

\newcommand*{\evaluate@foo}{%
  \@ifundefined{foo@\foo}{%
    \errmessage{\noexpand\foo has unknown value}%
  }{%
    \@nameuse{foo@\foo}%
  }%
}
\providecommand*{\foo}{}% initialization

% Test

\def\foo{baz}
\evaluate@foo

\def\foo{abc}
\evaluate@foo

\def\foo{xyz}
\evaluate@foo

\makeatletter

\begin{document}
\end{document}

结果:

\foo is baz
\foo is abc
! \foo has unknown value.

答案2

我能想到的最佳解决方案是expl3\str_case:nnTF在这里,我生成一个变体来获取一个标记而不是测试字符串,并与该标记的扩展列表进行比较。如果在列表中未找到匹配项,则输出相应的消息。

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\cs_generate_variant:Nn \str_case:nnF { VnF }

\cs_new_protected:Npn \artem_testfoo:N #1
 {
  \texttt{ \cs_to_str:N #1 } ~ 
  \str_case:VnF #1
   {
    { bar } { is ~ bar }
    { baz } { is ~ baz }
    { abc } { is ~ abc }
   }
   { is ~ neither ~ bar, ~ nor ~ baz, ~ nor ~ abc. }
 }

\NewDocumentCommand \testfoo { m }
 {
  \artem_testfoo:N #1
 }

\ExplSyntaxOff
\begin{document}

\def\foo{bar}
\testfoo\foo

\def\foo{baz}
\testfoo\foo

\def\foo{abc}
\testfoo\foo

\def\foo{xyz}
\testfoo\foo

\end{document}

在此处输入图片描述

答案3

xparse使用和相当容易expl3

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\xifstring}{mO{}m}
 {
  \str_case:onF { #1 } { #3 } { #2 }
 }
\ExplSyntaxOff

\begin{document}

\newcommand{\foo}{abc}

\xifstring{\foo}[No match]
 {
  {abc}{String is `abc'}
  {def}{String is `def'}
  {ghi}{String is `ghi'}
 }

\renewcommand{\foo}{def}

\xifstring{\foo}[No match]
 {
  {abc}{String is `abc'}
  {def}{String is `def'}
  {ghi}{String is `ghi'}
 }

\renewcommand{\foo}{ghi}

\xifstring{\foo}[No match]
 {
  {abc}{String is `abc'}
  {def}{String is `def'}
  {ghi}{String is `ghi'}
 }

\renewcommand{\foo}{foo}

\xifstring{\foo}[No match]
 {
  {abc}{String is `abc'}
  {def}{String is `def'}
  {ghi}{String is `ghi'}
 }

\end{document}

可选参数表示当没有匹配时您想要的操作(默认值,不执行任何操作)。

在此处输入图片描述

答案4

我的简单方法其他答案避免嵌套。这里我们没有TeX类似\if..\fi但合适的包装器,所以我们需要稍微修改一下定义。

\long\def\DoThis #1#2\OrThat #3{#1}
\long\def\OrThat #1{#1}% \@firstofone

\ifdefstring{\foo}{bar}{\DoThis{do something}}{}%
\ifdefstring{\foo}{baz}{\DoThis{do something else}}{}%
\ifdefstring{\foo}{bat}{\DoThis{do something different}}{}%
\OrThat {in case all tests fail}%

备注:由于\DoThis宏的定义,一旦测试结果为正,则所有后续测试都会被跳过。

相关内容