将强制参数用于可选参数?

将强制参数用于可选参数?

我正在尝试定义一个接受两个参数的命令,其中一个是可选的。如果没有给出可选参数,我希望将强制参数用作可选参数。

例如:

\necommand{\foo}[2][#2]{#1 foo(#2)}

返回结果为:

IN: \foo[hello]{hi}      OUT: hello foo(hi)
IN: \foo{hi}             OUT: hi foo(hi)

理想情况下,最好保持简单并使用没有软件包的标准 LaTeX。提前致谢!

答案1

对此的经典方法是使用\@dblarg

\documentclass{article}

\makeatletter
\newcommand{\foo}{\@dblarg\ah@foo}
\def\ah@foo[#1]#2{#1 foo(#2)}
\makeatother

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

在此处输入图片描述

有了xparse它就更容易了:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{om}{%
  \IfNoValueTF{#1}{#2}{#1} foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

无论您需要可选参数,只要输入\IfNoValueTF{#1}{#2}{#1}

如果您已于 2017/02/10(或更高版本)发布,那么还有一种更巧妙的方法xparse:可选参数O可以将任何强制参数作为默认值:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand{\foo}{ O{#2} m }{%
  #1 foo(#2)%
}

\begin{document}

No optional argument: \foo{xyz}

Optional argument: \foo[abc]{xyz}

\end{document}

因此,我们告诉\foo,如果缺少可选参数(第一次调用),则强制参数#2也应用作 的值#1。在第二次调用中,给出了可选参数,因此将其替换#1

答案2

\NewDocumentCommand使用from 来做这件事相当容易xparse,用 检查可选参数 (o) 是否给出\IfValueTF{#1}{}{}

然而,使用 就没那么容易了\newcommand。定义一个没有参数的命令,比如用\foobar来检查\@ifnextchar[{}{}下一个字符是否为 a [,然后分支为一个使用[]{}参数的命令和另一个只使用强制参数的命令,即{}。这种技术称为“移动参数”。这种方式不需要其他额外的包,并应用 LaTeX 核心功能。唯一的“棘手”之处在于使用对\makeatletter...\makeatother

\@ifnextchar[查找[,如果找到,则该字符基本上会“移位”回去,以便\foobar@opt可以再次找到它作为带有可选参数的命令的开头(实际上,如果已找到,[则将其存储到临时宏中并针对分支进行扩展)true[

\documentclass{article}

\usepackage{xparse}

\makeatletter
\newcommand{\foobar}{%
  \@ifnextchar[{\foobar@opt}{\foobar@noopt}
}{}

\newcommand{\foobar@opt}[2][]{%
  #1 foo(#2)%
}

\newcommand{\foobar@noopt}[1]{%
  #1 foo(#1)%
}
\makeatother

\NewDocumentCommand{\foo}{om}{%
  \IfValueTF{#1}{%
    #1 foo(#2)%
  }{%
    #2 foo(#2)%
  }%
}


\begin{document}
\foo[hello]{hi}

\foo{hi}       


\foobar[hello]{hi}

\foobar{hi}       

\end{document}

在此处输入图片描述

答案3

您可以为可选参数提供一个默认值,并使用它来判断它是否首先被提供。

在此处输入图片描述

\documentclass{article}

\newcommand{\foo}[2][\relax]{%
  \ifx\relax#1\relax #2\else #1\fi
  ~foo(#2)%
}

\begin{document}

\foo[hello]{hi}

\foo[]{hi}

\foo{hi}       

\end{document}

上面我指定了可选参数,\relax如果未提供则默认为,然后检查\ifx\relax#1\relax位置#2#1\ifx比较以下两个标记是否等效。

答案4

我赞成 egreg 的回答,即使用的建议\@dblarg

沃纳建议根据比较来检查“默认值” \ifx

检查默认值与检查是否提供了可选参数略有不同,因为完全不提供可选参数的情况与明确提供默认值的可选参数的情况没有区别。

\ifx-比较意味着

  • \ifx两种比较都以不可扩展的方式通过定义和比较临时宏来完成
  • \ifx-comparison 将直接应用于形成默认值的标记和实际作为可选参数提供的标记。

在后一种情况下,\ifx-comparison 对可用作默认值的内容施加了一些限制,并且不是完全“防水”的:

  • 默认值不能由多个标记组成。
  • 由于\ifx-comparison 比较的是单个标记,而不是宏参数,因此\ifx在参数由多个标记组成的边缘情况下,-comparison 可能会以几种错误的方式被超越。
  • 对于包含不平衡或的\ifx参数的边缘情况,-comparison 也可能会被超越。\else\fi
  • \ifx\let- 可以通过使用等于默认值标记的控制序列标记或活动字符标记来超越比较。

如果要以可扩展的方式检查默认值,即不定义一些临时宏并\ifx对其进行比较,我建议

  • 要么将“空”作为默认值并检查是否为空:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% 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>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: [email protected])
    %%
    \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}%
    }%
    
    \newcommand{\foo}[2][]{%
      \UD@CheckWhetherNull{#1}{#2}{#1}~foo(#2)%
    }%
    
    \makeatother
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    Empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    在此处输入图片描述

  • 或者通过处理分隔参数的宏来检查默认值:

    \documentclass{article}
    \makeatletter
    %%=========================================================================
    %% Paraphernalia:
    %%    \UD@firstoftwo, \UD@secondoftwo
    %%.........................................................................
    \newcommand\UD@firstoftwo[2]{#1}%
    \newcommand\UD@secondoftwo[2]{#2}%
    %%-------------------------------------------------------------------------
    %% 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>
    %%
    %% A concern in his posting is that the argument is hit with \string
    %% after some expansions which in edge cases might result in unbalancing
    %% surrounding \if..\fi-constructs if the macro is used inside of such
    %% \if..\fi-constructs.
    %%
    %% That challenging concern sickened me. ;-)
    %%
    %% Therefore I decided to implerment a variant where this cannot happen
    %% as expansion is forced by \romannumeral:
    %%
    %% After the first expansion-step, \string is not applied yet.
    %% After the second expansion-step, any possibly disturbing remainders
    %% are already removed due to \romannumeral-expansion.
    %%
    %% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
    %% digit 0, space token for terminating \romannumeral-expansion,
    %% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
    %%
    %% May 20, 2016
    %%
    %% Ulrich Diez (e-mail: [email protected])
    %%
    \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}%
    }%
    %%-------------------------------------------------------------------------
    %% Check whether argument contains no exclamation-mark on top-brace-level:
    %%.........................................................................
    %% \UD@CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does not contain !>}%
    %%                  {<Tokens to be delivered in case that
    %%                    argument which is to be checked does contain !>}%
    \long\def\UD@RemoveToExclamationMark#1!{}%
    \long\def\UD@CheckWhetherNoExclamationMark#1{%
      \expandafter\UD@CheckWhetherNull\expandafter{\UD@RemoveToExclamationMark#1!}%
    }%
    %%-------------------------------------------------------------------------
    %% Fork depending on some tokens:
    %%.........................................................................
    %%\\CheckWhetherDefault{<Argument which is to be checked>}%
    %%           {<Tokens to be delivered in case argument is "Default value">}%
    %%           {<Tokens to be delivered in case argument is not "Default value">}%
    %%
    %% In case <Argument which is to be checked> is neither "case 1" nor
    %% "case 2" the phrase "Error: Unknown parameter ``<Argument which is
    %% to be checked>'' to \CheckWhetherDefault." will be delivered.
    %%
    \newcommand\@CheckWhetherDefault{}%
    \long\def\@CheckWhetherDefault%
       #1!!Default value!#2#3!!!!{#2}%
    \newcommand\CheckWhetherDefault[1]{%
      \romannumeral0%
        \UD@CheckWhetherNoExclamationMark{#1}{%
          \@CheckWhetherDefault
          !#1!Default value!{\UD@firstoftwo}% <- #1 is empty.
          !!#1!{\UD@firstoftwo}% <- #1 = Default value
          !!Default value!{\UD@secondoftwo}% <- #1 = something else without exclamation mark
          !!!!%
        }{\UD@secondoftwo}% <- #1 = something else with exclamation mark
    }%
    \makeatother
    
    \newcommand{\foo}[2][Default value]{%
      \CheckWhetherDefault{#1}{#2}%
                              {#1}%
      ~foo(#2)%
    }%
    
    \parindent=0ex
    \parskip=\bigskipamount
    
    \begin{document}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo{hi}|:\\
    \foo{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[Default value]{hi}|:\\
    \foo[Default value]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[]{hi}|:\\
    \foo[]{hi}
    
    ``Default value'' or empty optional argument  or no optional argument will be given---%
    \verb|\foo[{}]{hi}|:\\
    \foo[{}]{hi}
    
    A nice optional argument will be given---%
    \verb|\foo[hello]{hi}|:\\
    \foo[hello]{hi}
    
    \end{document}
    

    在此处输入图片描述

您还可以采用无聊的不可扩展的方式定义和\ifx比较临时宏:

\documentclass{article}

\makeatletter
\newcommand{\foo}[2][Default value]{%
  \begingroup
  \def\mytempa{#1}%
  \def\mytempb{Default value}%
  \ifx\mytempa\mytempb
    \expandafter\endgroup\expandafter\@secondoftwo
  \else
    \expandafter\@firstofone
  \fi
  {%
    \def\mytempb{}%
    \expandafter\endgroup\ifx\mytempa\mytempb
      \expandafter\@secondoftwo
    \else
      \expandafter\@firstoftwo
    \fi
    {#1}%
  }{#2}~foo(#2)%
}%
\makeatother

\parindent=0ex
\parskip=\bigskipamount

\begin{document}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[Default value]{hi}|:\\
\foo[Default value]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}

``Default value'' or empty optional argument  or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}

A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}

\end{document}

在此处输入图片描述

正如我之前所说的:在常见/通常情况下,我绝对更喜欢\@dblarg-thing 而不是任何这些方法。

相关内容