2

I feel a bit stupid, but I why can't I put macros inside the argument of my newcommand? And why can't my macro \pra work inside the align environment, while it definitely works if I just write manually the expanded macro?

My macro (to denote probabilities):

\newcommand*{\pr}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{#1}[\,#2\,]}}

That is supposed to produce something like that:

enter image description here

This works:

\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0} 

This fails:

\pr[\substack{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}

with the 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}
                                                  \]

And still stranger, inside an align equation, the version \pr of the macro that used to work now fails, while if I write everything manually it works...

Thanks!

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}

2
  • 1
    In general you can give macros as arguments, you just need to be a bit careful what you do with those arguments. Not all macros can deal with all inputs. \ifthenelse doesn't seem to be too happy with complex beasts like \substack (I think \ifthenelse's \equal tries to expand the strings it compares, which goes wrong because \substack is not expandable). I would replace \ifthenelse{\equal{#1}{}} with etoolbox's \ifblank{#1}, which does no expansion here.
    – moewe
    Mar 21, 2020 at 19:58
  • Whoa amazing, I don't even need to protect with \ifblank, and it also solves the issues in align environment! Thanks! Feel free to add an answer, I'll accept it.
    – tobiasBora
    Mar 21, 2020 at 20:04

3 Answers 3

2

In general you can give macros as arguments, but you may need to be a bit careful what you do with those arguments. Not all macros can deal with all inputs equally well.

\ifthenelse doesn't seem to be too happy with complex beasts like \substack. I think \ifthenelse's \equal test tries to expand the strings it compares, which goes wrong because \substack is not expandable. In that case a healthy does of \protect before unexpandable content may help, but that probably gets tedious after a while.

I would replace \ifthenelse{\equal{#1}{}} with etoolbox's \ifblank{#1}, which does no expansion and thus needs no additional help to deal with even complex stuff like \substack here.

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

The fact that \ifblank does not expand its argument while \equal does means that there are differences in the behaviour of the two tests.

Compare

\documentclass[]{article}
\usepackage{amsmath}
\usepackage{etoolbox}
\usepackage{ifthen}

\newcommand*{\imblank}{}

\begin{document}
\ifthenelse{\equal{\imblank}{}}
  {T}
  {F}

\ifblank{\imblank}
  {T}
  {F}
\end{document}
2

The \ifthenelse test is a bit fragile. There are much better ways to cope with empty optional arguments.

With xparse the test for a nonappearing optional argument is possible with the argument type o. See the “easy definition”, that I comment out because an even better one is possible, using \pr* instead of a different command to insert \substack when needed.

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

What happens? If the optional argument doesn't appear, \IfNoValueTF returns the true branch, otherwise the false branch. This is reversed with \IfValueTF. Here we can abbreviate to \IfValueT, because we don't need to do anything when the optional argument is missing.

Inside this conditional text we use another conditional: if * is present after \pr, \IfBooleanTF returns the true branch and the optional argument is surrounded by \substack. Otherwise the plain argument is used.

enter image description here

4
  • Thanks! I wanted to avoid using NewDocumentCommand because I add some bad experience where NewDocumentCommand needed a pretty recent version of xparse to handle correctly facultative arguments, and that version was not yet pushed on overleaf, but maybe the issues does not apply here. Thanks!
    – tobiasBora
    Mar 21, 2020 at 20:44
  • @tobiasBora The used features have been stable for a few years.
    – egreg
    Mar 21, 2020 at 20:52
  • Ok thanks. I can't remember which option was the bad one (maybe it was in fact the +b in NewDocumentEnvironment?). I can't accept two answers, but I really like your answer as well, thanks!
    – tobiasBora
    Mar 21, 2020 at 21:10
  • @tobiasBora Nowadays Overleaf uses TeX Live 2019, IIRC, so that should be no longer a problem.
    – egreg
    Mar 21, 2020 at 21:11
1

Need to \protect the \substack in an optional argument [when \ifthenelse is being used].

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

enter image description here

4
  • Thanks a lot! But I have two questions: 1) first, a minor question: can I make sure I don't need to protect my substack? 2) more interesting, do you have a solution for the second part of my question, where even protecting inside align statement fails? (sorry I added the second part while you was writting the answer, if needed I can create another question)
    – tobiasBora
    Mar 21, 2020 at 20:01
  • Actually, as noted in the comments, usingifblank from etoolbox has two advantages: I don't need to \protect, and everything works in align environment!
    – tobiasBora
    Mar 21, 2020 at 20:05
  • @tobiasBora It looks like you have your answer then...reformulate the check to not use \ifthenelse. Mar 21, 2020 at 20:07
  • Yes, sorry, and thanks for your help, I'll keep my thumb up!
    – tobiasBora
    Mar 21, 2020 at 20:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .