我有以下问题:我正在使用该包etoolbox
检查宏(例如\foo
)是否扩展为几个可能的字符串之一(例如,,,bar
或其他)。我需要根据字符串扩展为哪个字符串执行不同的代码。baz
abc
\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
宏的定义,一旦测试结果为正,则所有后续测试都会被跳过。