
Me siento un poco estúpido, pero ¿por qué no puedo poner macros dentro del argumento de mi newcommand
? ¿Y por qué mi macro no puede \pra
funcionar dentro del align
entorno, mientras que definitivamente funciona si escribo manualmente la macro expandida?
Mi macro (para denotar probabilidades):
\newcommand*{\pr}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{#1}[\,#2\,]}}
Se supone que eso produce algo como esto:
Esto funciona:
\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0}
Esto falla:
\pr[\substack{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}
con el error:
ERROR: Use of \\pr doesn't match its definition.
--- TeX said ---
\new@ifnextchar ...served@d = #1\def \reserved@a {
#2}\def \reserved@b {#3}\f...l.18 ...k{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}
\]
Y más extraño aún, dentro de una align
ecuación, la versión \pr
de la macro que antes funcionaba ahora falla, mientras que si escribo todo manualmente funciona...
¡Gracias!
MWE:
\documentclass[]{article}
\usepackage{amsmath}
\usepackage{ifthen}
\setlength{\parindent}{0pt}
\newcommand*{\pr}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{#1}[\,#2\,]}}
\newcommand*{\pra}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{\substack{#1}}[\,#2\,]}}
\begin{document}
This works:
\[\pr{a = 0 \mid b = 0}\]
\[\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
\[\Pr_{\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}} [a = 0 \mid b = 0]\]
This fails:
% \[\pr[\substack{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}\]
% \[\pr[\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}]{a = 0 \mid b = 0}\]
But if I put substack inside, it works:
\[\pra[a \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
\[\pra[a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
Now, it's still stranger: if I put the full expression without my macro, it works inside an align:
\begin{align}
\Pr_{\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}}[a = 0 \mid b = 0]
\end{align}
But if I use the macro that was used before, it fails:
% \begin{align}
% \pra[a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}]{a = 0 \mid b = 0}
% \end{align}
% even if I use protect:
%\begin{align}
% \pr[\protect\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}]{a = 0 \mid b = 0}
%\end{align}
\end{document}
Respuesta1
En general, puedes dar macros como argumentos, pero es posible que tengas que tener un poco de cuidado con lo que haces con esos argumentos. No todas las macros pueden manejar todas las entradas igualmente bien.
\ifthenelse
No parece estar muy contento con bestias complejas como \substack
. Creo \ifthenelse
que la \equal
prueba intenta expandir las cadenas que compara, lo cual sale mal porque \substack
no es expandible. En ese caso, una buena dosis de \protect
contenido no expandible puede ayudar, pero probablemente se vuelva tedioso después de un tiempo.
Lo reemplazaría \ifthenelse{\equal{#1}{}}
con etoolbox
's \ifblank{#1}
, que no se expande y, por lo tanto, no necesita ayuda adicional para lidiar incluso con cosas complejas como \substack
aquí.
\documentclass[]{article}
\usepackage{amsmath}
\usepackage{etoolbox}
\newcommand*{\pr}[2][]{%
\ifblank{#1}
{\Pr[\,#2\,]}
{\Pr_{#1}[\,#2\,]}}
\begin{document}
\begin{align}
\pr[\substack{a \leftarrow \{0,1\}\protect\\b \leftarrow \{0,1\}}]{a = 0 \mid b = 0}
\end{align}
\end{document}
El hecho de que \ifblank
no amplíe su argumento aunque \equal
sí significa que existen diferencias en el comportamiento de las dos pruebas.
Comparar
\documentclass[]{article}
\usepackage{amsmath}
\usepackage{etoolbox}
\usepackage{ifthen}
\newcommand*{\imblank}{}
\begin{document}
\ifthenelse{\equal{\imblank}{}}
{T}
{F}
\ifblank{\imblank}
{T}
{F}
\end{document}
Respuesta2
La \ifthenelse
prueba es un poco frágil. Hay formas mucho mejores de lidiar con argumentos opcionales vacíos.
Con xparse
la prueba de un argumento opcional que no aparece es posible con el tipo de argumento o
. Vea la “definición fácil”, que comento porque es posible una aún mejor, usando \pr*
en lugar de un comando diferente para insertar \substack
cuando sea necesario.
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
%%% Easy version
%\NewDocumentCommand{\pr}{om}{%
% \IfNoValueTF{#1}
% {% no optional argument
% \Pr[\,#2\,]%
% }
% {% optional argument is expressed
% \Pr_{#1}[\,#2\,]%
% }%
%}
%%% Better version
\NewDocumentCommand{\pr}{som}{%
% * = use substack
% #2 = optional
% #3 = mandatory
\Pr\IfValueT{#2}{_{\IfBooleanTF{#1}{\substack{#2}}{#2}}}[\,#3\,]
}
\begin{document}
\begin{gather*}
\pr{a = 0 \mid b = 0}
\\
\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0}
\\
\pr*[a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}]{a = 0 \mid b = 0}
\end{gather*}
\end{document}
¿Lo que sucede? Si el argumento opcional no aparece, \IfNoValueTF
devuelve la rama verdadera; en caso contrario, la rama falsa. Esto se invierte con \IfValueTF
. Aquí podemos abreviar como \IfValueT
, porque no necesitamos hacer nada cuando falta el argumento opcional.
Dentro de este texto condicional usamos otro condicional: si *
está presente después de \pr
, \IfBooleanTF
devuelve la rama verdadera y el argumento opcional está rodeado por \substack
. De lo contrario, se utiliza el argumento simple.
Respuesta3
Es necesario \protect
incluir \substack
un argumento opcional [cuando \ifthenelse
se utiliza].
\documentclass[]{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{ifthen}
\setlength{\parindent}{0pt}
\newcommand*{\pr}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{#1}[\,#2\,]}}
\newcommand*{\pra}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{\substack{#1}}[\,#2\,]}}
\begin{document}
This works:
\[\pr{a = 0 \mid b = 0}\]
\[\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
\[\Pr_{\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}} [a = 0 \mid b = 0]\]
This [no longer] fails with \verb|\protect|:
\[\pr[\protect\substack{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}\]
\[\pr[\protect\substack{a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}}]{a = 0 \mid b = 0}\]
But if I put substack inside, it works:
\[\pra[a \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
\[\pra[a \leftarrow \{0,1\}\\b \leftarrow \{0,1\}]{a = 0 \mid b = 0}\]
\end{document}