math shorthand macro: avoid extra space from empty superscript

I find myself writing $\{0,1\}^x$ a whole lot, so I want to define a shorthand macro:

\def\zo#1{\{0,1\}^{#1}}


However, sometimes I need $\{0,1\}$ with no superscript. If I write \zo{} I get a tiny bit of extra space after the close brace (because there's an empty superscript in there). The only way I have found to get rid of it is

\def\zo#1{\{0,1\}\if\relax\detokenize{#1}\relax\else^{#1}\fi}


which is rather a mouthful. Is there a more elegant way to accomplish this? Note that the actual document I am writing is in LaTeX, so if amsmath (for instance) has something to the purpose, that would be a fine answer. I have expressed the question in plain TeX because it is fundamentally a question about the core math engine.

If you can't see the tiny bit of extra space, this MWE (requires (pdf)etex) will make it obvious:

\def\a#1{\{0,1\}^{#1}}
\def\b#1{\{0,1\}\if\relax\detokenize{#1}\relax\else^{#1}\fi}
\leavevmode\rlap{$\a{}\a{}\a{}\a{}$}$\b{}\b{}\b{}\b{}$
\bye


→ (400% zoom)

-
+1 despite the missing MWE, the question is very nicely stated! – yo' Jan 26 '13 at 20:52
@tohecz There's an MWE right there after the paragraph that begins "If you can't see the tiny bit of extra space". Perhaps it does not look like an MWE to you because you're used to LaTeX? In plain TeX there is no required preamble. – zwol Jan 26 '13 at 21:06
oh sorry, right. It makes it a bit confusing and it's not clear whether then David's solution qualifies, and my solution using \@ifnextchar, but ok :) – yo' Jan 26 '13 at 21:07
@tohecz LaTeX solutions are okay (I did say so) but a Plain-only solution is more likely to get the checkmark. None of the answers so far strike me as more elegant than what I've got now. – zwol Jan 26 '13 at 21:11
@Zack A: Chat message are never going to be deleted, you can link to them in a comment or in your question. B: Proposal: \def\a{\{0,1\}} and use verbose ^{<stuff>} if needed … – Qrrbrbirlbel Jan 26 '13 at 21:26

Solution A

Use

\def\a{\{0,1\}}


and add ^{<stuff>} if needed.

Solution B

Notes

• \csname <something else>\endcsname expands to \relax if \<something else> is not defined.
• No other macro must be named \qrrgobbletwo<something> where <something> could be an argument for \a.
• All other “tricks” and helpers used in the other answers ( \newcommand with optional argument/\@ifnextchar) can still be applied. (But then I would use an conditional anyway!)

Code

\def\qrrgobbletwo#1#2{}
\def\a#1{%
\{0,1\}\csname qrrgobbletwo\detokenize{#1}\endcsname^{#1}%
}
\def\b#1{\{0,1\}\if\relax\detokenize{#1}\relax\else^{#1}\fi}
\leavevmode\rlap{$\a{}\a{}\a{}\a{}$}$\b{}\b{}\b{}\b{}$

\leavevmode\rlap{$\a{a}\a{a}\a{a}\a{a}$}$\b{a}\b{a}\b{a}\b{a}$
\bye


Output

-
Try \a{\frac{1}{2}}. :) – egreg Jan 26 '13 at 22:39
@egreg Try again. (I added a \detokenize.) Anyway, I would just use 1. an optional argument and 2. a conditional or 3. Solution A. – Qrrbrbirlbel Jan 26 '13 at 23:00
Of course your solution A is the best, as I also wrote in my answer. – egreg Jan 26 '13 at 23:07
I went with your Solution A in the end. – zwol Jan 29 '13 at 3:04

This seems to be an optional feature so should be an optional argument:

 \newcommand\zo[1][]{\{0,1\}\ifx\relax#1\relax\else^{#1}\fi}

\zo   \zo[x]

-
Er, how does the optional-ness of the argument get you out of having to use \if and \detokenize? Also I can't really call this more elegant than what I've already got, since it is fundamentally the same construct, but with all the additional magic required for optional-argument processing. – zwol Jan 26 '13 at 21:09
The optional or mandatory argument choice is independent of the method used to test for an empty argument (there are some questions on this site about pros and cons of different empty tests) It is just that in the no-subscript case \zo is cleaner markup (and more idiomatic latex) than \zo{}, and if the argument is optional latex syntax guidelines would use [] rather than {}. I don't think \if and \detokenize are a normal test: I'd normally use the one I used or the one tohecz used testing with \ifx against \@empty. – David Carlisle Jan 26 '13 at 21:40

Using e.g. xparse you'll get more readable code:

\usepackage{xparse}
\DeclareDocumentCommand\zo{g}{%
\ensuremath{%
\{0,1\}%
\IfNoValueTF{#1}{}{^{#1}}%
}%
}


Using {o} as an argument specifier instead of {g} would make the command use an optional ([]-delimited) argument instead, which may be more appropriate.

-
(Although \zo{} will still yield the errant spacing, simply \zo works and will be correctly spaced) – You Jan 26 '13 at 20:56
That means that the argument {} is optional? That is very confusing, the variant with {o} seems certainly better. – yo' Jan 26 '13 at 20:58
Exactly. Since the original intention is an optional argument, the {o} variant is much better. – You Jan 26 '13 at 21:00
I regret to say that I cannot accept any answer involving xparse (or expl3, or even keyval, or anything else that makes fundamental changes to the way one writes macros), because I'm sure it's a wonderful thing if you write packages all day, but there is no more space in my brain for TeX programming tricks. – zwol Jan 26 '13 at 21:13
\makeatletter
\newcommand\zo[1]{\begingroup\def\x{#1}\ifx\x\@empty\{0,1\}\else\{0,1\}^{\x}\fi\endgroup}
\makeatother


We collect the argument by \def\x{#1} and then test if it's empty by \ifx\x\@empty. The rest should be clear. This solution is a bit better than yours because we read #1 only once. If might be a good idea to make it robust by replacing \newcommand with \DeclareRobustCommand, so that you can use it in strange contexts as section titles as well.

To make it purely plainTeX, you can do the following:

\def\zo#1{\begingroup\def\x{#1}\def\y{}\ifx\x\y\{0,1\}\else\{0,1\}^{\x}\fi\endgroup}


Example:

\makeatletter
\newcommand\zo[1]{\begingroup\def\x{#1}\ifx\x\@empty\{0,1\}\else\{0,1\}^{\x}\fi\endgroup}
\newcommand\oldzo[1]{\{0,1\}^{#1}}
\makeatother

\noindent
$\zo{}\zo{}\zo{}\zo{}$\\
$\oldzo{}\oldzo{}\oldzo{}\oldzo{}$\\
$\{0,1\}\{0,1\}\{0,1\}\{0,1\}$


Variant with an optional argument:

\makeatletter
\def\zo@opt[#1]{\{0,1\}^{#1}}
\newcommand\zo{\@ifnextchar[\zo@opt{\{0,1\}}}
\makeatother

$\zo[2] \neq \zo \zo$

-
I do not see that either of these solutions is more elegant than what I have now. You got rid of the \detokenize, so that's something, but now it's not fully expandable anymore, and really what I am looking for here is a construct that doesn't need a conditional. – zwol Jan 26 '13 at 21:19

You can use the clever conditional tests by Donald Arsenau discussed in Is "conditionals" name of package?

{\catcode\@=11 % @ is a letter
\catcode\!=8  % funny catcode so ! will be a delimiter
\catcode\Q=3  % funny catcode so Q will be a delimiter
\long\gdef\given#1{88\fi\Ifbl@nk#1QQQ\empty!}
\long\gdef\blank#1{88\fi\Ifbl@nk#1QQ..!}% if null or spaces
\long\gdef\nil#1{\IfN@Ught#1* {#1}!}% if null
\long\gdef\IfN@Ught#1 #2!{\blank{#2}}
\long\gdef\Ifbl@nk#1#2Q#3!{\ifx#3}% same as above
}
\def\kmsp{\kern-\scriptspace}

\def\zo#1{\{0,1\}^{\if\blank{#1}\aftergroup\kmsp\else#1\fi}}

$\zo{}\zo{}$\quad$\zo{2}\zo{2}$

$\{0,1\}\{0,1\}$\quad$\{0,1\}^2\{0,1\}^2$

\bye


Of course this is overkill, but shows why a small space creeps in: it's called \scriptspace and is added by TeX when there's a (possibly empty) superscript or subscript.

A simpler definition would be (again using \blank):

\def\zo#1{\{0,1\}\if\blank{#1}\else^{#1}\fi


This works even with \zo{ }.

However, I don't find any convenience of writing \zo{2} instead of \zo^{2}, considering also that you are forced to put in the braces when there's no exponent. So, the best definition is, in my opinion,

\def\zo{\{0,1\}}


with following input as

$\zo$
$\zo^{2}$


and so on. No tests, no problems. This is "Solution A" in Qrrbrbirlbel's answer.

Simple LaTeX solution:

\usepackage{xparse}
\NewDocumentCommand{\zo} { o } {\{0,1\}\IfValueT{#1}{^{#1}}}


to be used as

$\zo$
$\zo[2]$

-

Here is a partially functional solution based on meta-conditionals:

\def\zo{\{0,1\}\xzo}
\def\xzo#1{\xxzo #1\empty^\empty}
\def\xxzo#1#2#3{#3{#1}}

\let\a\zo
\def\b#1{\{0,1\}\if\relax\detokenize{#1}\relax\else^{#1}\fi}
\leavevmode\rlap{$\a{}\a{}\a{}\a{}$}$\b{}\b{}\b{}\b{}$

\def\x{123}
$\zo{1} \zo{\x} \zo\alpha$
\bye


You can verify (running with etex) that this does not create phantom space with an empty argument. The only catch is that if you want a multi-token exponent, you have to frame it in a temporary macro because the "trick" requires that the argument have exactly zero or one tokens.

The way the trick works is that when TeX picks up undelimited arguments, it doesn't have any way of knowing where the end of one is except by looking at where the next one begins. So we have a meta-conditional, where which arguments become #1, #2, and #3 in \xxzo depends on whether the #1 that is placed in \xzo is empty or one token. If the former, then #1 = \empty, #2 = ^, and #3 = \empty, and \xxzo expands to almost nothing (the braces with no content are a box with zero width). If the latter, then #1 = #1, #2 = \empty, and #3 = ^, and you get the superscript. Effectively, the absence of the putative first argument pushes the ^ into the "ignored" slot.

I tried for a while to make it work with arbitrary arguments to \zo`, but that would appear to require a delimited argument, and the entire basis for the trick fails there since you have to place a delimiter that prevents the argument numbers from shifting. Any suggestions would be welcome.

-