
Estou tentando definir um comando que aceita dois argumentos, sendo um opcional. Se o argumento opcional não for fornecido, gostaria que o argumento obrigatório fosse usado para o argumento opcional.
Por exemplo:
\necommand{\foo}[2][#2]{#1 foo(#2)}
que retornaria:
IN: \foo[hello]{hi} OUT: hello foo(hi)
IN: \foo{hi} OUT: hi foo(hi)
Idealmente, seria bom mantê-lo simples e usar o LaTeX padrão sem pacotes. Desde já, obrigado!
Responder1
A abordagem clássica para isso é usar \@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}
Com xparse
isso é mais fácil:
\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}
Sempre que você precisar do argumento opcional, digite \IfNoValueTF{#1}{#2}{#1}
.
Há uma maneira muito mais inteligente se você xparse
lançou 10/02/2017 (ou posterior): um argumento opcional O
pode tomar como padrão qualquer um dos argumentos obrigatórios:
\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}
Então estamos dizendo \foo
que, se faltar o argumento opcional (primeira chamada), o argumento obrigatório #2
deve ser usado também como valor para #1
. Na segunda chamada, o argumento opcional é fornecido, portanto é substituído por #1
.
Responder2
Isso é muito fácil com \NewDocumentCommand
from xparse
, verificando se o argumento opcional (o) foi fornecido ou não com \IfValueTF{#1}{}{}
.
Não é tão fácil com \newcommand
, no entanto. Defina um comando sem argumentos, diga \foobar
e verifique \@ifnextchar[{}{}
se o próximo caractere é a [
e ramifica para um comando que usa []{}
argumentos e outro, que usa apenas o argumento obrigatório, ou seja {}
, . Esta técnica é chamada de 'argumentos móveis'. Desta forma, não precisa de outros pacotes extras e aplica os principais recursos do LaTeX. O único ponto 'complicado' é usar o \makeatletter...\makeatother
par.
\@ifnextchar[
procura [
e se for encontrado, o caractere é basicamente 'deslocado' de volta para que \foobar@opt
possa encontrá-lo novamente como início de um comando com argumento opcional (na verdade, [
é armazenado em uma macro temporária e expandido para a true
ramificação, se [
tiver sido encontrado)
\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}
Responder3
Você pode fornecer um valor padrão para o argumento opcional e usá-lo para condicionar se ele foi ou não fornecido em primeiro lugar.
\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}
Acima, especifiquei que o argumento opcional será padronizado \relax
se não for fornecido e, em seguida, marque \ifx\relax#1\relax
place #2
ou #1
. \ifx
compara os dois tokens a seguir quanto à equivalência.
Responder4
Sou a favor da resposta de egreg, ou seja, a sugestão de usar \@dblarg
.
Werner sugeriu verificar um "valor padrão" em termos de \ifx
comparação.
Verificar um valor padrão é um pouco diferente de verificar se um argumento opcional foi fornecido, pois o caso do argumento opcional não ser fornecido não se distingue do caso do argumento opcional ser fornecido explicitamente com o valor padrão.
\ifx
-comparação implica que
- qualquer uma das comparações deve ser feita de forma não expansível, definindo e
\ifx
comparando macros temporárias - ou
\ifx
-comparison deve ser aplicado diretamente ao token que forma o valor padrão e ao(s) token(s) realmente fornecido(s) como argumento opcional.
Neste último caso, \ifx
-comparison impõe algumas restrições sobre o que pode ser usado como valor padrão e não é completamente "à prova d'água":
- Os valores padrão não podem consistir em vários tokens.
- Como
\ifx
-comparison compara tokens únicos, não argumentos macro,\ifx
-comparison pode ser superado de várias maneiras errôneas em casos extremos em que os argumentos consistem em mais de um token. - Uma
\ifx
comparação também pode ser superada pelo caso extremo de argumentos contendo\else
ou desequilibrados\fi
. \ifx
-as comparações podem ser superadas usando tokens de sequência de controle ou tokens de caracteres ativos\let
iguais ao token de valor padrão.
Se seguir o caminho de verificar um valor padrão de forma expansível, ou seja, não definir algumas macros temporárias e \ifx
compará-las, sugiro
tendo "vazio" como valor padrão e verificando o vazio:
\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}
ou verificando o valor padrão por meio de macros que processam argumentos delimitados:
\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}
Você também pode seguir o caminho chato e não expansível de definir e \ifx
comparar macros temporárias:
\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}
Como eu disse antes: Em situações comuns/habituais eu definitivamente prefiro o \@dblarg
-thing a qualquer uma dessas abordagens.