回想任意位置参数

回想任意位置参数

我试图回忆一个基于另一个论点的论点,即

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

在此处输入图片描述

在此处输入图片描述

相关内容