
我試圖定義一個帶有兩個參數的命令,其中一個是可選的。如果未給予可選參數,我希望將強制參數用於可選參數。
例如:
\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
答案4
我贊成egreg的答案,即使用的建議\@dblarg
。
沃納建議根據比較檢查“預設值” \ifx
。
檢查預設值與檢查是否提供了可選參數有些不同,因為根本沒有提供可選參數的情況與明確提供預設值的可選參數的情況沒有區別。
\ifx
-比較意味著
\ifx
任一比較都透過定義和比較臨時巨集以不可擴展的方式完成- 或
\ifx
-comparison 將直接應用於形成預設值的標記和實際作為可選參數提供的標記。
在後一種情況下\ifx
-comparison 對可用作預設值的內容施加了一些限制,並且不完全「防水」:
- 預設值不能由多個標記組成。
- 由於
\ifx
-comparison 確實比較單一標記,而不是巨集參數,因此\ifx
在參數包含多個標記的邊緣情況下, -comparison 可能會以幾種錯誤的方式被擊敗。 \ifx
包含不平衡\else
或 的參數的邊緣情況也可以擊敗-比較\fi
。\ifx
\let
- 可以透過使用控制序列標記或等於預設值標記的活動字元標記來戰勝比較。
如果以可擴展的方式檢查預設值,即不定義一些臨時巨集並\ifx
比較它們,我建議
將“emptiness”作為預設值並檢查是否為空:
\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 勝過任何這些方法。