0

Suppose that I have an xparse function like

\NewDocumentCommand \prob { m o }
    {\mathbb{P}\!\left(#1\IfValueT{#2}{\;\middle\vert\;#2}\right)}

and I want to make a second version of the function \probsplit which looks like

\NewDocumentCommand \probsplit { >{\SplitArgument{1}{|}}m }
    {\prob #1}

By itself, this does not seem to work (as it would if \prob took two mandatory arguments). Typing \probsplit{x|y} for instance produces something like $P(x)y$, while \probsplit{x} produces something like $P(x)-NoValue-$.

One solution to this dilemma is to make a helper function as follows (and call it from \probsplit:

\NewDocumentCommand \probsplithelper { m m }
    {\prob{#1}[#2]}

However, it seems there must be a better way. I have two questions:

  1. Why does the original \probsplit not work as intended / why does it behave in the unintended way that it does?

  2. What is a more direct way to pass the output of \SplitArgument to a function with optional arguments?

3
  • 1
    Answer to 1.: Because \SplitArgument will store something like {#1.1}{#1.2} inside of #1 (with #1.1 being the first split and #1.2 the second) and if no split was possible it'll store {#1}{<novalue-marker>}. Your \prob looks for a second argument in brackets, which isn't there.
    – Skillmon
    Feb 25, 2021 at 7:39
  • Does that mean that if I try to use d{} instead of o, it should work?
    – Grayscale
    Feb 25, 2021 at 8:38
  • No, d{} wouldn't work. If you explicitly load xparse you can use the g type, which is an optional argument in braces, but you really shouldn't, those types are only kept for backwards compatibility and their usage is no good practice. Instead, you should probably change your \prob macro to use an internal that takes two normal arguments. You could then use the same internal with your splitted argument, I'll write an answer with a quick sketch of what I mean.
    – Skillmon
    Feb 25, 2021 at 9:15

1 Answer 1

1

Instead of trying to match the syntax of the optional argument taking macro, use a unified internal macro that takes two normal arguments, and the front-facing macros to parse the user-input and forward it to the internal. That would give a clean syntax (though not programmatically extensible to an arbitrary number of arguments with an arbitrary syntax).

\documentclass[]{article}

\usepackage[]{amssymb}

\newcommand\probinternal[2]
  {\mathbb{P}\!\left(#1\IfValueT{#2}{\;\middle\vert\;#2}\right)}
\NewDocumentCommand \prob { m o }{\probinternal{#1}{#2}}
\NewDocumentCommand \probsplit { >{\SplitArgument{1}{|}}m }{\probinternal#1}

\begin{document}
$\prob{A}$\par
$\prob{A}[B]$\par
$\probsplit{A}$\par
$\probsplit{A|B}$
\end{document}
5
  • Mm okay. Is there any way to make the internal commands "private" so that they are not visible after, say, using \usepackage on the file they are contained in?
    – Grayscale
    Feb 27, 2021 at 21:23
  • @Grayscale TeX has no namespaces, and no real private macros. There is the convention of using @ in package-internal macros' names.
    – Skillmon
    Feb 27, 2021 at 23:59
  • I see. Also, in what way is your solution different from the one in the question that creates a helper function /probsplithelper?
    – Grayscale
    Feb 28, 2021 at 0:04
  • @Grayscale in that it creates a common internal for both syntax alternatives, instead of providing a wrapper for the second syntax alternative to bring the arguments into a form accepted by the first alternative. Imho, the solution with a common internal is cleaner and better structured.
    – Skillmon
    Feb 28, 2021 at 0:23
  • Mm makes sense.
    – Grayscale
    Feb 28, 2021 at 1:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.