我试图回忆一个基于另一个论点的论点,即
\newcommand{\switchtwo}[3]{#(#3)}
如果第三个参数是 1,则应该返回 #1;如果第三个参数是 2,则应该返回 #2。有什么办法可以做到这一点吗?
答案1
您可以使用\ifcase
:
\newcommand{\switchtwo}[3]{%
\ifcase#3 \or#1\or#2\fi
}
如果第三个参数既不是 1 也不是 2,则宏将不返回任何内容,但如果第三个参数不是整数,则会出现错误或垃圾。
你的想法行不通,因为定义中的位置参数是以特殊方式存储的,你不能将替换文本与它们混合。
答案2
您可以构建正确形式的内部命令
\documentclass{article}
\def\switchtwo#1#2#3{%
\def\tmp##1##2{###3}%
\tmp{#1}{#2}}
\usepackage[T1]{fontenc}
\begin{document}
\switchtwo{aaa}{bbb}{1}
\switchtwo{\string\or}{\string\fi}{2}
\end{document}
答案3
\newcommand{\switchtwo}[3]{#(#3)}
— 您所想到的语法意味着可以在扩展宏时使用“动态”表示宏的参数。#⟨whatsoever⟩
在 (La)TeX 中这是不可能的:
只有在定义宏时才能使用#1
等#2
来表示其参数。
在扩展时用 来表示它的一个参数是不可能的。#⟨whatsoever⟩
在展开的时候,⟨parameter-text⟩
宏的定义被当做一组指令进行逐一收集参数来自标记流:
当发生扩展时\switchto
,LaTeX 从标记流中收集参数:首先,它从标记流中收集由定义文本的参数表示的参数/标记集#1
。然后,它从标记流中收集由定义文本的参数表示的参数/标记集#2
。然后,它从标记流中收集由定义文本的参数表示的参数/标记集#3
。然后,它将这些标记集插入到替换文本中,并将其返回到标记流。
你能用 LaTeX 做什么?
大卫卡莱尔在他的回答中如何 在宏的定义文本中使用##⟨whatsoever⟩
用于“动态表示”的其中一个参数其他宏,一个临时宏,在定义其他/临时宏。
专注于定义临时宏的方法不能在扩展上下文中使用。
其他可能性(也可以在扩展上下文中使用)包括:
只要确保第三个参数是⟨number⟩
TeXbook 意义上的 — — 该数量的确切含义⟨number⟩
在 TeXbook 第 24 章:垂直模式摘要中讨论 — —,你大概可以这样做:
\documentclass{article}
\makeatletter
\newcommand{\switchtwo}[3]{%
\romannumeral0\ifnum\number\number0#3=1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\@firstoftwo\@secondoftwo}{%
\ifnum\number\number0#3=2 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\@secondoftwo\@secondoftwo}%
{\@firstoftwo}%
}%
{ Error: Arg 3 is neither 1 nor 2}{{ #1}{ #2}}%
}%
\makeatother
\begin{document}
\switchtwo{A}{B}{1}
\switchtwo{A}{B}{2}
\switchtwo{A}{B}{3}
\switchtwo{A}{B}{}
\hrulefill
\newcounter{foobar}
\setcounter{foobar}{1}%
\switchtwo{A}{B}{\number\value{foobar}}
\setcounter{foobar}{2}%
\switchtwo{A}{B}{\number\value{foobar}}
\setcounter{foobar}{3}%
\switchtwo{A}{B}{\number\value{foobar}}
\end{document}
在上面的例子中,\romannumeral0...
不用于获取数字的罗马数字表示。\romannumeral0...
用于在搜索更多数字或空格标记时触发大量扩展工作,该工作将被丢弃,因为它只是终止了数字序列,因此也终止了对更多数字的搜索。确保在所有这些扩展工作之后找到一个空格标记,从而终止数字序列。0
因此,在完成所有扩展工作后,会找到一个非正数,而\romannumeral
会默默地吞下非正数而不返回任何标记。
请注意,对 的第三个参数中的任意标记序列是否形成TeXbook 意义上的\switchtwo
有效进行可靠测试是不可行的:⟨number⟩
任意的 token 序列可能形成任意的基于扩展的算法。因此,这样的测试包括测试任意基于扩展的算法的结果是否形成⟨number⟩
TeXbook 意义上的有效结果。这反过来意味着测试任意基于扩展的算法是否完全终止(没有错误消息)。这反过来又意味着要解决停机问题。艾伦·马西森·图灵于 1936 年证明,不存在一种通用算法来解决所有可能的程序输入对的停机问题。
\switchto
如果您希望检查第三个参数中包含的标记集(不进行任何扩展/评估)是否恰好由 catcode-12-(other)-character-token 组成1
,或者恰好由 catcode-12-(other)-character-token2
或其他内容组成,则可以通过分隔参数来完成:
\documentclass{article}
\makeatletter
%%-----------------------------------------------------------------------------
%% 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\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% \UD@DigitFork{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is formed by a set of
%% tokens which contains only the catcode-12-character 1>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is formed by a set of
%% tokens which contains only the catcode-12-character 2>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is formed by a set of
%% tokens which is empty or contains something else>}%
%%
\@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}%
\@ifdefinable\UD@DigitSelect{\long\def\UD@DigitSelect#1!!1!2!#2#3!!!!{#2}}%
\newcommand\UD@DigitFork[4]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}{%
\UD@DigitSelect
!#1!1!2!{#4}% empty
!!#1!2!{#2}% 1
!!1!#1!{#3}% 2
!!1!2!{#4}% something else without !
!!!!%
}{#4}% something else with !
}%
%%-----------------------------------------------------------------------------
%% \switchto
\newcommand\switchtwo[3]{%
\romannumeral0\UD@DigitFork{#3}{ #1}{ #2}{ Error: Arg 3 is neither 1 nor 2}%
}%
\makeatother
\begin{document}
\switchtwo{A}{B}{1}
\switchtwo{A}{B}{2}
\switchtwo{A}{B}{3}
\switchtwo{A}{B}{}
\hrulefill
Be aware that this variant of \verb|\switchto| does not evaluate its third argument
and that the token-set \verb|\number\value{foobar}| in any case neither does
equal the token \texttt{1$_{12}$} nor does equal the token \texttt{2$_{12}$}:
\newcounter{foobar}
\setcounter{foobar}{1}%
\switchtwo{A}{B}{\number\value{foobar}}
\setcounter{foobar}{2}%
\switchtwo{A}{B}{\number\value{foobar}}
\setcounter{foobar}{3}%
\switchtwo{A}{B}{\number\value{foobar}}
\end{document}
顺便一提:
此答案中提出的所有测试均未定义临时宏。
因此,本答案中提出的测试也适用于扩展环境。
例如,您可以做类似的事情\csname @\switchtwo{first}{second}{1}oftwo\endcsname{first}{second}
。
通过此答案中提出的测试,第一和第二个参数的放置方式\switchtwo
使得参数中不平衡的\csname
/ \endcsname
/ \if...
/ \or
/ \else
/\fi
不会造成干扰。
例如,您可以做类似的事情\csname @\switchtwo{\ifx AA}{\ifx AB}{1}first\else second\fi oftwo\endcsname{first}{second}
。
\if..
但请注意,大括号/组嵌套与.. \else
..匹配无关\fi
,因此这些事情可能会干扰周围\if..
.. \else
..结构的.. ..匹配。\fi
\if..
\else
\fi
答案4
您的问题是关于选择一些论点。
该问题可以看作是从 L 个连续的非分隔参数中选择第 K 个参数的问题的一个特例。
如果您对此感兴趣,我可以提供一个可扩展的例程\KeepKthOfLArguments
让您做到这一点。
\documentclass[a4paper, landscape]{article}
\makeatletter
%%=============================================================================
%% Layout of this example:
%%=============================================================================
\pagestyle{plain}%
\@ifundefined{pageheight}{}{\pageheight=\paperheight}%
\@ifundefined{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\@ifundefined{pagewidth}{}{\pagewidth=\paperwidth}%
\@ifundefined{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\oddsidemargin=1.5cm
\topmargin=\oddsidemargin
\textwidth=\paperwidth
\advance\textwidth-2\oddsidemargin
\marginparsep=.375cm
\marginparwidth=\oddsidemargin
\advance\marginparwidth-2\marginparsep
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\headheight=0ex
\headsep=0ex
\textheight=\paperheight
\advance\textheight-2\topmargin
\footskip=.5\topmargin
\advance\footskip.5\ht\strutbox
\advance\topmargin-1in
%%-----------------------------------------------------------------------------
\makeatother
\makeatletter
%% Code for \KeepKthOfLArguments:
%%=============================================================================
%% 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]{%
\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}%
}%
%%=============================================================================
%% Keep only the K-th of L consecutive undelimited arguments.
%% ( IF K < 1 OR K > L just remove L consecutive undelimited arguments. )
%%=============================================================================
%% \KeepKthOfLArguments{<integer number K>}%
%% {<integer number L>}%
%% <sequence of L consecutive undelimited arguments>
%%
%% If L < 1 yields nothing.
%% Else:
%% If K >= 1 and K < L yields:
%% <K-th undelimited argument from <sequence of L consecutive undelimited
%% arguments>>
%% If K < 1 or K > L
%% (-> there is no K-th argument in the
%% <sequence of L consecutive undelimited arguments> )
%% yields nothing but removal of <sequence of L consecutive
%% undelimited arguments>
\newcommand\KeepKthOfLArguments[2]{%
\romannumeral0%
% #1: <integer number K>
% #2: <integer number L>
\expandafter\UD@KeepKthOfLArgumentsKSmallerOneFork
\expandafter{\romannumeral\number\number#1 000\expandafter}%
\expandafter{\romannumeral\number\number#2 000}%
}%
%%-----------------------------------------------------------------------------
\newcommand\UD@KeepKthOfLArgumentsKSmallerOneFork[2]{%
% #1: <K letters m>
% #2: <L letters m >
\UD@CheckWhetherNull{#1}{% K is smaller than one:
\UD@KeepKthOfLArgumentsRemoveNArguments{#2}{ }{}%
}{% K is not smaller than one:
\expandafter\UD@PassFirstToSecond
\expandafter{%
\UD@firstoftwo{}#1%
}{%
\UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop{#1}{#2}%
}{#2}%
}%
}%
%%-----------------------------------------------------------------------------
\newcommand\UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop[4]{%
% #1: <K letters m>
% #2: <L letters m>
% (For detecting whether K>L or K<=L, during the loop letters m will
% be removed both from #1 and #2 until at least one of these arguments
% is empty.
% When the loop terminates with 0<K<=L, #1 will be empty and #2
% will hold an amount of letters m corresponding to the the
% difference L-K.
% When the loop terminates with K>L, #1 will not be empty and #2
% will be empty.
% )
% #3: <K-1 letters m>
% #4: <L letters m>
% (#3 and #4 will be left untouched during the loop so they can be
% used for performing appropriate action when loop terminates as
% it is known whether K>L.)
\UD@CheckWhetherNull{#1}{% We have K<=L:
\UD@KeepKthOfLArgumentsRemoveNArguments{%
#3%
}{%
\UD@KeepKthOfLArgumentsRemoveNArguments{#2}{ }%
}{}%
}{%
\UD@CheckWhetherNull{#2}{% We have K>L:
\UD@KeepKthOfLArgumentsRemoveNArguments{#4}{ }{}%
}{% We don't know yet whether K<=L or K>L, thus remove letters m and
% do another iteration:
\expandafter\UD@PassFirstToSecond
\expandafter{%
\UD@firstoftwo{}#2%
}{%
\expandafter\UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop
\expandafter{%
\UD@firstoftwo{}#1%
}%
}{#3}{#4}%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% \UD@KeepKthOfLArgumentsRemoveNArguments{<N letters m>}%
%% {<argument 1>}%
%% {<argument 2>}%
%% <sequence of consecutive
%% undelimited arguments>
%%.............................................................................
%% Removes the first N undelimited arguments from the <sequence of
%% consecutive undelimited arguments>, then inserts
%% <argument 1><argument 2>
%%
%% On the one hand when providing <argument 2> empty, you can use
%% <argument 1> for nesting calls to \UD@KeepKthOfLArgumentsRemoveNArguments.
%% On the other hand you can provide a <space token> for stopping
%% \romannumeral-expansion as <argument 1> and have the
%% macro grab the <K-th undelimited argument> from the <sequence of L
%% consecutive undelimited arguments> as <argument 2>.
%%
\newcommand\UD@KeepKthOfLArgumentsRemoveNArguments[3]{%
%% #1: <N letters m>
%% #2: <Argument 1>
%% #3: <Argument 2>
\UD@CheckWhetherNull{#1}{#2#3}{%
\UD@firstoftwo{%
\expandafter\UD@KeepKthOfLArgumentsRemoveNArguments
\expandafter{%
\UD@firstoftwo{}#1%
}{#2}{#3}%
}%
}%
}%
%%-----------------------------------------------------------------------------
%% End of code for \KeepKthOfLArguments.
\makeatother
\begin{document}
\footnotesize
\noindent\texttt{%
\string\KeepKthOfLArguments\allowbreak\{1\}\allowbreak\{30\}\allowbreak\{Word 1\}\allowbreak\{Word 2\}%
\allowbreak\{Word 3\}\allowbreak\{Word 4\}\allowbreak\{Word 5\}\allowbreak\{Word 6\}\allowbreak\{Word 7\}%
\allowbreak\{Word 8\}\allowbreak\{Word 9\}\allowbreak\{Word 10\}\allowbreak\{Word 11\}\allowbreak\{Word 12\}%
\allowbreak\{Word 13\}\allowbreak\{Word 14\}\allowbreak\{Word 15\}\allowbreak\{Word 16\}\allowbreak\{Word 17\}%
\allowbreak\{Word 18\}\allowbreak\{Word 19\}\allowbreak\{Word 20\}\allowbreak\{Word 21\}\allowbreak\{Word 22\}%
\allowbreak\{Word 23\}\allowbreak\{Word 24\}\allowbreak\{Word 25\}\allowbreak\{Word 26\}\allowbreak\{Word 27\}%
\allowbreak\{Word 28\}\allowbreak\{Word 29\}\allowbreak\{Word 30\}%
} yields:
\KeepKthOfLArguments{1}{30}{Word 1}{Word 2}{Word 3}{Word 4}{Word 5}{Word 6}%
{Word 7}{Word 8}{Word 9}{Word 10}{Word 11}{Word 12}%
{Word 13}{Word 14}{Word 15}{Word 16}{Word 17}{Word 18}%
{Word 19}{Word 20}{Word 21}{Word 22}{Word 23}{Word 24}%
{Word 25}{Word 26}{Word 27}{Word 28}{Word 29}{Word 30}%
\newcount\loopcnt
\loopcnt=2 %
\loop
\vskip\partopsep\vskip\topsep
\noindent\texttt{%
\string\KeepKthOfLArguments\allowbreak\{\number\loopcnt\}\allowbreak\{30\}\allowbreak\{Word 1\}\allowbreak\{Word 2\}%
\allowbreak\{Word 3\}\allowbreak\{Word 4\}\allowbreak\{Word 5\}\allowbreak\{Word 6\}\allowbreak\{Word 7\}%
\allowbreak\{Word 8\}\allowbreak\{Word 9\}\allowbreak\{Word 10\}\allowbreak\{Word 11\}\allowbreak\{Word 12\}%
\allowbreak\{Word 13\}\allowbreak\{Word 14\}\allowbreak\{Word 15\}\allowbreak\{Word 16\}\allowbreak\{Word 17\}%
\allowbreak\{Word 18\}\allowbreak\{Word 19\}\allowbreak\{Word 20\}\allowbreak\{Word 21\}\allowbreak\{Word 22\}%
\allowbreak\{Word 23\}\allowbreak\{Word 24\}\allowbreak\{Word 25\}\allowbreak\{Word 26\}\allowbreak\{Word 27\}%
\allowbreak\{Word 28\}\allowbreak\{Word 29\}\allowbreak\{Word 30\}%
} yields:
\KeepKthOfLArguments{\number\loopcnt}{30}{Word 1}{Word 2}{Word 3}{Word 4}{Word 5}{Word 6}%
{Word 7}{Word 8}{Word 9}{Word 10}{Word 11}{Word 12}%
{Word 13}{Word 14}{Word 15}{Word 16}{Word 17}{Word 18}%
{Word 19}{Word 20}{Word 21}{Word 22}{Word 23}{Word 24}%
{Word 25}{Word 26}{Word 27}{Word 28}{Word 29}{Word 30}%
\par
\ifnum\loopcnt<30 \advance\loopcnt by 1 %
\repeat
\vskip\partopsep\vskip\topsep
\noindent\verb|\KeepKthOfLArguments| does deliver the result after two
expansion-steps:
\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\KeepKthOfLArguments{3}{11}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}%
}%
\end{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
\KeepKthOfLArguments{3}{11}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}%
}%
\noindent\texttt{\string\test: \meaning\test}%
\end{document}
使用例程\KeepKthOfLArguments
可以定义\switchtwo
如下:
\newcommand\switchtwo[3]{\KeepKthOfLArguments{#3}{2}{#1}{#2}}%