
Я чувствую себя немного глупо, но почему я не могу поместить макросы внутрь аргумента моего newcommand
? И почему мой макрос не может \pra
работать внутри align
среды, хотя он определенно работает, если я просто вручную напишу расширенный макрос?
Мой макрос (для обозначения вероятностей):
\newcommand*{\pr}[2][]{\ifthenelse{\equal{#1}{}}{\Pr[\,#2\,]}{\Pr_{#1}[\,#2\,]}}
Предполагается, что это произведет что-то вроде этого:
Это работает:
\pr[b \leftarrow \{0,1\}]{a = 0 \mid b = 0}
Это не удается:
\pr[\substack{a \leftarrow \{0,1\}}]{a = 0 \mid b = 0}
с ошибкой:
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}
\]
И что еще более странно, внутри align
уравнения версия \pr
макроса, которая раньше работала, теперь дает сбой, хотя если я все пропишу вручную, то все работает...
Спасибо!
МВЭ:
\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}
решение1
В общем, вы можете задать макросы в качестве аргументов, но вам, возможно, придется быть немного осторожнее с тем, что вы делаете с этими аргументами. Не все макросы могут одинаково хорошо работать со всеми входными данными.
\ifthenelse
похоже, не слишком доволен сложными зверями вроде \substack
. Я думаю, \ifthenelse
что \equal
тест пытается расширить сравниваемые строки, что не получается, потому что \substack
не расширяется. В этом случае \protect
может помочь здоровая does of до нерасширяемого содержимого, но это, вероятно, станет утомительным через некоторое время.
Я бы заменил \ifthenelse{\equal{#1}{}}
на etoolbox
's \ifblank{#1}
, который не расширяется и, таким образом, не нуждается в дополнительной помощи, чтобы справиться даже со сложными вещами, как \substack
здесь.
\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}
Тот факт, что \ifblank
не расширяет свой аргумент, в то время как \equal
делает, означает, что существуют различия в поведении двух тестов.
Сравнивать
\documentclass[]{article}
\usepackage{amsmath}
\usepackage{etoolbox}
\usepackage{ifthen}
\newcommand*{\imblank}{}
\begin{document}
\ifthenelse{\equal{\imblank}{}}
{T}
{F}
\ifblank{\imblank}
{T}
{F}
\end{document}
решение2
Тест \ifthenelse
немного хрупкий. Есть гораздо лучшие способы справиться с пустыми необязательными аргументами.
С xparse
тестом на неявный необязательный аргумент возможно с типом аргумента o
. Смотрите «простое определение», которое я закомментировал, потому что возможно даже лучшее, используя \pr*
вместо другой команды для вставки \substack
при необходимости.
\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}
Что происходит? Если необязательный аргумент не появляется, \IfNoValueTF
возвращает истинную ветвь, в противном случае ложную ветвь. Это обратно с \IfValueTF
. Здесь мы можем сократить до \IfValueT
, потому что нам не нужно ничего делать, когда необязательный аргумент отсутствует.
Внутри этого условного текста мы используем еще одно условие: если *
присутствует после \pr
, \IfBooleanTF
возвращает истинную ветвь, а необязательный аргумент окружен \substack
. В противном случае используется простой аргумент.
решение3
Необходимо \protect
использовать \substack
необязательный аргумент [когда \ifthenelse
используется].
\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}