我定义了一个新命令\case
:
\documentclass{article}
\usepackage{pgffor}
\newcommand\case[1]{
\ifx 1#1 case 1\else
\ifx 2#1 case 2\else
\ifx 3#1 case 3\else
Illegal
\fi\fi\fi
}
\begin{document}
\case{1}
\case{2}
\case{3}
\end{document}
新的命令运行良好:
\case{1}: case 1
\case{2}: case 2
\case{3}: case 3
但对于每个结果:
\foreach \x in {1,2,3}{\case{\x}}
非法 非法 非法
任何提示都将不胜感激。
答案1
这个问题与你无关,foreach
你会看到同样的问题\def\x{1} \case{\x}
您正在比较\ifx 1\x
,并且标记 1 和\x
并不相同,所以这是错误的。
相反地,\case{12}
将\ifx112
测试 1 和 1 为真,然后输入 2。
你想做一个数字测试而不是测试未扩展的标记,所以
\documentclass{article}
\newcommand\case[1]{%
\ifcase\numexpr#1\relax
case 0\or
case 1\or
case 2\or
case 3\else
Illegal%
\fi}
\usepackage{tikz}
\begin{document}
\case{1}
\case{12}
\case{3}
\foreach \x in {1,2,3}{\case{\x}}
\end{document}
答案2
我可以提出一组更灵活的命令。
\documentclass{article}
\usepackage{pgffor}
%\usepackage{xparse} % uncomment if using LaTeX released before 2020-10-01
\ExplSyntaxOn
\NewExpandableDocumentCommand{\checkcases}{mmO{}m}
{
\int_case:nnTF { #1 } { #2 } { #3 } { #4 }
}
\ExplSyntaxOff
\newcommand{\mycaseswitch}[1]{%
\checkcases{#1}{
{1}{Case 1}
{2}{Case 2}
{3}{Case 3}
{42}{Ultimate}
}{Illegal}%
}
\newcommand{\otherswitch}[1]{%
\checkcases{#1}{
{-2}{Negative}
{2}{Positive}
}[!]{Bad?}%
}
\begin{document}
\mycaseswitch{1}: case 1
\mycaseswitch{2}: case 2
\mycaseswitch{3}: case 3
\mycaseswitch{4}: illegal
\mycaseswitch{42}: Hey!
\foreach \x in {1,2,3}{\mycaseswitch{\x}\par}
\otherswitch{-2}
\otherswitch{2}
\otherswitch{0}
\end{document}
该\checkcases
命令只是内部expl3
函数的接口\int_case:nnTF
。它有四个参数:
- 根据案例列表进行测试的整数;
- 案例列表,以对的形式出现
{<integer>}{<text>}
(不需要整数连续或像 那样从 0 开始\ifcase
);对之间的空格将被忽略; - (可选)匹配成功后使用的文本;
- 表示“无匹配”的文本。
答案3
在您的代码中,\foreach
-loop 将标记\x
作为参数传递给\case
-macro。\ifx
(在 的定义文本内\case
)会比较标记的含义而不扩展它们。
所以
- 通过将
\ifx
显式字符标记的含义与控制序列标记的含义进行比较,尽管含义不同,112
\x
- 通过将
\ifx
显式字符标记的含义与控制序列标记的含义进行比较,尽管含义不同,212
\x
- 通过将
\ifx
显式字符标记的含义与控制序列标记的含义进行比较,虽然含义不同。312
\x
因此事情最终落入了“非法”分支。
确保\foreach
作为循环变量的控制序列正确扩展取决于用户。
尝试:\foreach \x in {1,2,3}{\expandafter\case\expandafter{\x}}
顺便一提:
\case
如果有人提供#1
不平衡的内容\else
和/或与-expressions\fi
匹配的内容,则可能会出现意想不到的行为\ifx
⟨定义文本⟩ 的\case
。例如,尝试一下\case{4\fi\iftrue}
。
为了在实现它时获得乐趣,我可以提供一个\ExtractKthArg
完全可扩展的宏,不需要任何额外的包,也不需要任何附加包,\if..\fi
并且不会被不平衡\else
或\fi
在其参数范围内所欺骗:
句法:
\ExtractKthArg{⟨TeX-⟨number⟩-quantity denoting an integer of value K⟩}%
{⟨tokens in case list of undelimited arguments doesn't have a K-th argument⟩}%
{⟨list of undelimited arguments⟩}%
如果⟨无界参数列表⟩ :
是否送货⟨如果未限定参数列表没有第 K 个参数,则使用 tokens⟩。
假设有第 K 个参数⟨无界参数列表⟩ :
传递第 K 个参数并删除一层括号。
例子:
\ExtractKthArg{0}{not available}{ABCDE}
产量:not available
\ExtractKthArg{3}{not available}{ABCDE}
产量: C
\ExtractKthArg{3}{not available}{AB{CD}E}
产量: CD
\ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}}
产量:004
\ExtractKthArg{6}{not available}{{001}{002}{003}}
产量:not available
只要你的\case
宏的参数在任何情况下都是有效的 TeX-⟨数字⟩-数量你想要 TeX-⟨数字⟩- 数量值从 1 到 3 是“合法的”,您可以\case
通过以下方式轻松实现\ExtractKthArg
:
\newcommand\case[1]{%
\ExtractKthArg{#1}{illegal}{{case 1}{case 2}{case 3}}%
}%
代码如下:
\documentclass{article}
\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% 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\z@\UD@secondoftwo}%
{\expandafter\z@\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<TeX-<number>-quantity denoting an integer of value K>}%
%% {<tokens in case list of undelimited arguments doesn't have a K-th argument>}%
%% {<list of undelimited arguments>} %
%%
%% In case there is no K-th argument in <list of undelimited arguments> :
%% Does deliver <tokens in case list of undelimited arguments doesn't have a K-th argument>.
%% In case there is a K-th argument in <list of undelimited arguments> :
%% 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: <TeX-<number>-quantity denoting an integer of value K>
% #2: <tokens in case list of undelimited arguments doesn't have a K-th argumnent>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{% K not positive:
\z@#2%
}{% K positive:
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#3{}.}{\z@#2}{%
\UD@CheckWhetherNull{#1}{%
\UD@ExtractFirstArgLoop{#3\UD@SelDOm}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}}%
}%
}%
}%
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\z@\UD@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%% End of code for \ExtractKthArg.
\makeatother
\usepackage{pgffor}
\newcommand\case[1]{%
\ExtractKthArg{#1}{illegal}{{case 1}{case 2}{case 3}}%
}%
\pagestyle{empty}
\begin{document}
\verb|\case{1}|: \case{1}
\verb|\case{2}|: \case{2}
\verb|\case{3}|: \case{3}
\verb|\case{-1}|: \case{-1}
\verb|\case{4}|: \case{4}
The \verb|\foreach|-loop:
\verb|\foreach \x in {1,2,3}{\case{\x}}|:
\foreach \x in {1,2,3}{\case{\x}}
\end{document}
如果不能确保你的论点\case
宏的参数在任何情况下都是有效的 TeX-⟨数字⟩-数量,事情更加复杂:
严格地说,不可能测试任意宏参数是否产生有效的 TeX-⟨数字⟩-quantity:TeX 被认为是图灵完备的。任意一个宏参数都会形成一个任意基于扩展的算法。检查任意基于扩展的算法是否产生有效的 TeX-⟨数字⟩-quantity 意味着检查所讨论的任意基于扩展的算法(由图灵完备的系统执行)是否完全终止。这就是停机问题。艾伦·图灵于 1936 年证明,不存在解决所有可能的程序输入对的停机问题的通用算法。
但是如果你将标记星座的 -eh- 项目符号列表限制为“合法”的用户输入,那么你可以实现从单个“合法”星座到数字的映射,并在调用时使用该映射\ExtractKthArg
:
假设你只希望 token-constellation和 token-constellation和 token-constellation和 token-constellation和 token-constellation和 token-constellation被允许作为合法的用户输入, 两者和产生短语, 两者和产生短语, 两者和产生短语。 你可以通过分隔参数实现分叉/映射,如下所示:A11112
a11112
B11212
b11212
C11312
c11312
A11112
a11112
case a1 or A1
B11212
b11212
case b2 or B2
C11312
c11212
case c3 or C3
标记(-序列)不是任何被视为合法用户输入的标记星座的组成部分。因此,此标记(-序列)可用作参数分隔符的组成部分。但您需要测试用户传递的参数是否包含此标记(-序列),以确保在用户提供的内容以某种错误的方式与参数分隔符匹配时不会发生奇怪的事情:!12
% Code for a mechanism which forks illegal user-input and the
% different cases of legal user-input and maps these cases to
% numbers:
\@ifdefinable{\GobbleToExclam}{\long\def\GobbleToExclam#1!{}}%
\@ifdefinable\CaseSelect{%
\long\def\CaseSelect#1!a1!A1!b2!B2!c3!C3!#2#3!!!{#2}%
}%
\newcommand\MapCasesToNumbers[1]{%
% Check whether an argument contains no exclamation-mark of
% catcode 12 which is not nested in braces:
\expandafter\UD@CheckWhetherNull\expandafter{\GobbleToExclam#1!}{%
% Something without exclamation-mark, so let's select a case by
% means of \CaseSelect where exclamation-mark is a component of
% the argument-delimiter:
\CaseSelect
!#1!A1!b2!B2!c3!C3!{1}% <- Case #1 = a1
!a1!#1!b2!B2!c3!C3!{1}% <- Case #1 = A1
!a1!A1!#1!B2!c3!C3!{2}% <- Case #1 = b2
!a1!A1!b2!#1!c3!C3!{2}% <- Case #1 = B2
!a1!A1!b2!B2!#1!C3!{3}% <- Case #1 = c3
!a1!A1!b2!B2!c3!#1!{3}% <- Case #1 = C3
!a1!A1!b2!B2!c3!C3!{4}% <- Case #1 = s.th. else without exclamation-mark
!!!%
}{4}% <- Case #1 = s.th. else with exclamation-mark
}%
% End of code for a mechanism which forks illegal user-input
% and the different cases of legal user-input and maps these
% cases to numbers.
如果您这样做,则要确保\pgffor
在应用分叉机制之前正确扩展作为循环变量的宏等,这取决于您。
综合起来:
\documentclass{article}
\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% 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\z@\UD@secondoftwo}%
{\expandafter\z@\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<TeX-<number>-quantity denoting an integer of value K>}%
%% {<tokens in case list of undelimited args doesn't have a K-th argument>}%
%% {<list of undelimited arguments>} %
%%
%% In case there is no K-th argument in <list of undelimited arguments> :
%% Does deliver <tokens in case list of undelimited arguments doesn't have a K-th argument>.
%% In case there is a K-th argument in <list of undelimited arguments> :
%% 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: <TeX-<number>-quantity denoting an integer of value K>
% #2: <tokens in case list of undelimited arguments doesn't have a K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{% K not positive:
\z@#2%
}{% K positive:
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#3{}.}{\z@#2}{%
\UD@CheckWhetherNull{#1}{%
\UD@ExtractFirstArgLoop{#3\UD@SelDOm}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}}%
}%
}%
}%
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\z@\UD@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%% End of code for \ExtractKthArg.
\makeatother
\makeatletter
% Code for a mechanism which forks illegal user-input and the
% different cases of legal user-input and maps these cases to
% numbers:
\@ifdefinable{\GobbleToExclam}{\long\def\GobbleToExclam#1!{}}%
\@ifdefinable\CaseSelect{%
\long\def\CaseSelect#1!a1!A1!b2!B2!c3!C3!#2#3!!!{#2}%
}%
\newcommand\MapCasesToNumbers[1]{%
% Check whether an argument contains no exclamation-mark of
% catcode 12 which is not nested in braces:
\expandafter\UD@CheckWhetherNull\expandafter{\GobbleToExclam#1!}{%
% Something without exclamation-mark, so let's select a case by
% means of \CaseSelect where exclamation-mark is a component of
% the argument-delimiter:
\CaseSelect
!#1!A1!b2!B2!c3!C3!{1}% <- Case #1 = a1
!a1!#1!b2!B2!c3!C3!{1}% <- Case #1 = A1
!a1!A1!#1!B2!c3!C3!{2}% <- Case #1 = b2
!a1!A1!b2!#1!c3!C3!{2}% <- Case #1 = B2
!a1!A1!b2!B2!#1!C3!{3}% <- Case #1 = c3
!a1!A1!b2!B2!c3!#1!{3}% <- Case #1 = C3
!a1!A1!b2!B2!c3!C3!{4}% <- Case #1 = s.th. else without exclamation-mark
!!!%
}{4}% <- Case #1 = s.th. else with exclamation-mark
}%
% End of code for a mechanism which forks illegal user-input
% and the different cases of legal user-input and maps these
% cases to numbers.
\makeatother
\usepackage{pgffor}
\newcommand\case[1]{%
\ExtractKthArg{\MapCasesToNumbers{#1}}{illegal case}{{case a1 or A1}{case b2 or B2}{case c3 or C3}}%
}%
\pagestyle{empty}
\begin{document}
\verb|\case{a1}|: \case{a1}
\verb|\case{A1}|: \case{A1}
\verb|\case{b2}|: \case{b2}
\verb|\case{B2}|: \case{B2}
\verb|\case{c3}|: \case{c3}
\verb|\case{C3}|: \case{C3}
\verb|\case{Something Weird \relax \csname \fi}|: \case{Something Weird \relax \csname \fi}
\verb|\case{D4}|: \case{D4}
The \verb|\foreach|-loop---you need to ensure that the loop-variable \verb|\x| is expanded properly:
\verb|\foreach \x in {a1,A1,b2,B2,c3,C3,Weird}{\expandafter\case\expandafter{\x}}|:
\foreach \x in {a1,A1,b2,B2,c3,C3,Weird}{\expandafter\case\expandafter{\x}}
\end{document}