14

Let's suppose I want to write nested equations that contain a fair few nested parenthetical/delimiter characters, like (), [], {}, ||, and perhaps others. Suppose too that I think these things look nicer if the outer delimiters are a tad bigger than the inner ones (where possible), appearing to "frame" them.

I can write things like this:

$\pi_G\big(f\big(\big[[v]_{\sim}\big]_{\sim''}\big)\big)$

But this \big(...\big) stuff is kind of ugly and gets in the way of seeing, from the LaTeX code, what's going on. What might be better is if I could write:

$\pi_G\mybig(){f\mybig(){\mybig[]{[v]_{\sim}}_{\sim''}}}$

Here, the () after \mybig mean that the macro argument should be surrounded by \big(...\big). If it's [], then the argument should be surrounded by \big[...\big], and so on.

But wait! This can in fact be done; it is only necessary to define the macro with three arguments, like the following:

\newcommand{\mybig}[3]{\big{#1}#3\big{#2}}

How nice. But unfortunately, this doesn't work so well when I want to use braces, as in

$\mybig{}{(x)}$

Here, {} is actually a group, and constitutes only one argument. So this fails. Of course, the following both work, but mean that uses of the macro are less uniform in appearance (which is not so pleasing):

$\mybig{}{}{(x)}$
$\mybig\{\}{(x)}$

So what I really need is to test whether argument #1 is empty (indicating that the macro-invocation was immediately followed by an empty group, i.e. {}). If so, the macro "skips" to the third argument, and typesets \big\{#3\big\}. Is there a way to do this?

1

5 Answers 5

6

Here's another LaTeX3 version.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/131136/86}
\usepackage{xparse}

\ExplSyntaxOn

\cs_new_nopar:Npn \paren_set:nnnn #1#2#3#4
{
  \use:c {#1} #2 #4 \use:c {#1} #3
}

\cs_new_nopar:Npn \paren_get_first:nn #1#2
{
  \tl_if_empty:nTF {#1}
  {
    \paren_set:nnnn {big} {\lbrace} {\rbrace} {#2}
  }
  {
    \tl_if_single:nTF {#1}
    {
      \paren_set:nnnn {big} {#1} {#2}
    }
    {
      \tl_if_empty:nTF {#2}
      {
        \paren_set:nnnn {#1} {\lbrace} {\rbrace}
      }
      {
        \paren_set:nnnn {#1} {#2}
      }
    }
  }
}

\DeclareDocumentCommand \MyBig { }
{
  \paren_get_first:nn
}

\ExplSyntaxOff

\begin{document}

\[
\MyBig(){\frac12} \quad
\MyBig[]{\frac12} \quad
\MyBig{}{\frac12} \quad
\MyBig(.{\frac12} \quad
\MyBig\{.{\frac12} \quad
\MyBig{Big}(){\frac12} \quad
\MyBig{bigg}[]{\frac12} \quad
\MyBig{Bigg}{}{\frac12} \quad
\MyBig(.{\frac12} \quad
\MyBig\{.{\frac12}
\]

\end{document}

This uses the following logic:

The typesetting command takes in four arguments: the size, the left delimiter, the right delimiter, and the content. The despatcher has to work out what these should be from the input. The choices are:

  1. \MyBig{}{Content}. Use big for the size and \lbrace, \rbrace for the delimiters.
  2. \MyBig(){Content}. Use big for the size and (, ) for the delimiters.
  3. \MyBig{bigg}{}{Content}. Use bigg for the size and \lbrace, \rbrace for the delimiters.
  4. \MyBig{bigg}(){Content}. Use bigg for the size and (, ) for the delimiters.

So the despatcher does the following:

  1. Read in two arguments. This is safe since there will always be at least two.
  2. Check the first one. Is it empty? If so, the command must have been \MyBig{}{Content} so we typeset that as \big\{<Content>\big\}. End here.
  3. If the first is not empty, we test if it is a singleton. If so, the argument must have been a delimiter. The second will then be the closing delimiter and we have yet to get the content. So we call the typesetter with {big}{#1}{#2} and it will take the content from the stream.
  4. If the first is not a singleton, it is a size argument. The second is thus the delimiter so we test on that. If it is empty, our delimiters are braces so we call the typesetter with {#1}{\lbrace}{\rbrace} and the next group is the content.
  5. We're now in the situation that the first is not a singleton and the second is not empty. So the second is a delimiter, and we have to get the closing delimiter and the content from the stream so we call the typesetter with {#1}{#2} and let it get the rest from the stream.

delimiters

Note that delimiters don't have to match, but if you want an unmatched brace you have to use \{ or \} (or \lbrace or \rbrace).

1
  • Note: it would be possible to make the size options use the full command (rather than the command name) but then the tests would have to be a bit different, and a bit more complicated. Commented Sep 2, 2013 at 7:59
6

Using LaTeX3, xparse, and a bit of trickery (explained), the effect is very possible.

We set up a document-level interface macro called \MyBig (capitalized in keeping with current conventions). This macro can take three arguments:

  • an optional argument delimited by <>,
  • a 'normal' 'optional argument' used to test for the desire for the [] delimiter, and
  • a mandatory argument to check if we were passed an empty argument, signaling the need for the {} delimiters.

Thus, the document-level macro's only purpose is to dispatch to other functions that scan the input stream further for the needed tokens—the dispatcher decides which macro gets input stream, and the macro decides how many tokens it wants. (I don't believe there is any way to strictly have a switch that asks for more tokens, and if there is, it certainly isn't pretty.)

output

\documentclass{article}
\usepackage{expl3,xparse}

\ExplSyntaxOn

\msg_new:nnnn { mybig } { invalid-argument }
{ Argument~ `#1`~ invalid. }
{ The~ apparent~ optional~ argument~ should~ never~ take~ a~ value;~
  it~ is~ solely~ for~ the~ purpose~ of~ delimiter~ specification.}

% Typeset, in braces of size #1, the content in #2
\cs_new:Npn \MyBig_braces #1 #2
 {
  #1\{ #2 #1\}
 }

% Typeset, in brackets of size #1, the content in #2
\cs_new:Npn \MyBig_brackets #1 #2
 {
  #1[ #2 #1]
 }

% Typeset, in delimiters #2 and #3 of size #1, the content in #4
\cs_new:Npn \MyBig_other #1 #2 #3 #4
 {
  #1#2 #4 #1#3
 }

 % If the (second) optional argument is empty, we were passed the
 % delimiters [], so dispatch.  If the optional argument is not empty
 % and has a value, then something went horribly wrong.  Otherwise
 % (NoValue), check to see if the mandatory argument is empty.  If it
 % is, we want the {} delimiter, so dispatch.  Otherwise, dispatch the
 % generic handler.
\NewDocumentCommand \MyBig { D<>{\Big} o m }
 {
  \str_if_eq_x:nnTF { #2 } { }
   { \MyBig_brackets { #1 } { #3 } }
   {
    \IfValueTF { #2 }
     { \msg_error:nnn { mybig } { invalid-argument } { #2 } }
     {
      \str_if_eq_x:nnTF { #3 } { }
        { \MyBig_braces { #1 } }
        { \MyBig_other { #1 } { #3 } }
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\[
\MyBig(){\frac12} \quad
\MyBig[]{\frac12} \quad
\MyBig{}{\frac12} \quad
\MyBig<\big>{}{\frac12} \quad
\MyBig<\Bigg>[]{\MyBig{}{\int_0^\infty}}
\]

% \[ \MyBig<\Bigg>[bad]{\MyBig{}{\int_0^\infty}} \]

\end{document}
3
  • Note you can change the delimiters to what seems most appropriate for you (such as backtick, !, or @), although if you delimit by < and > instead of \langle and \rangle, typographers will judge you :-). Related: tex.stackexchange.com/questions/2132. This practice is called currying. See also lazy evaluation. Commented Sep 2, 2013 at 15:23
  • Thanks for your answer. There was very little in between yours, Andrew Stacey's and egreg's! I chose Andrew's because it was the one I made use of in the end and because I appreciated his very detailed explanation.
    – Hammerite
    Commented Sep 2, 2013 at 18:30
  • @Hammerite perfectly alright! I enjoyed solving the problem :) Commented Sep 2, 2013 at 18:44
6

You could do something along these lines by testing for the presence of a starred form of the command in which case \{ and \} are used.

\makeatletter
\def\mybig{\@ifstar{\@mybig}{\@@mybig}}
\def\@mybig#1{\left\{#1\right\}}
\def\@@mybig#1#2#3{\left#1#3\right#2}
\makeatother

then the following code

\[\mybig[]{abc}\]

\[\mybig*{\mybig{\langle}{\rangle}{abc}}\]

\[\mybig.{|}{\int_a^b 2x\mathrm{d}x = x^2}_{a}^{b}\]

will produce something like

enter image description here

UPDATE

Here's some code that tests for an empty argument.

\documentclass{article}

\makeatletter
\def\mybig#1{%%"
  \def\@my@temp{#1}%%
  \ifx\@my@temp\@empty\expandafter\@mybig\else
                      \expandafter\@@mybig\expandafter\@my@temp\fi}
\def\@mybig#1{\left\{#1\right\}}
\def\@@mybig#1#2#3{\left#1#3\right#2}
\makeatother

\begin{document}

\[\mybig[]{abc}\]

\[\mybig{}{\mybig{\langle}{\rangle}{abc}}\]

\[\mybig[]{ \int_a^b 2x\mathrm{d}x }^2\]

\end{document}

enter image description here

But this is sort of thing is not fail-safe!

3
  • This technique is a decent alternative approach, but I wonder if it is possible to get a macro that works in just the way I said.
    – Hammerite
    Commented Sep 2, 2013 at 3:08
  • @Hammerite I've updated the answer to something closer to what you want.
    – A.Ellett
    Commented Sep 2, 2013 at 5:46
  • Thanks very much for the time you spent helping. As you can see, I had a wealth of alternatives to choose from and I chose Andrew Stacey's, but I appreciate your help.
    – Hammerite
    Commented Sep 2, 2013 at 18:32
6

This set of macros uses the following strategy:

  1. we open a group, so that nested calls will not clobber the auxiliary macros meaning;

  2. we store the optional argument (in brackets) in the macro \withdel@size, default \big;

  3. we call \withdel@check that absorbs all the tokens up to the first open brace (which must be present); these tokens should be a pair of delimiters, that are recognized and stored in \withdel@left and \withdel@right respectively;

  4. we call \withdel@process that typesets the formula and closes the group.

\documentclass{article}
\makeatletter
\newcommand{\withdel}[1][\big]{%
  \begingroup\def\withdel@size{#1}%
  \withdel@check
}
\def\withdel@check#1#{%
  \withdel@getdelims{#1}%
  \withdel@process
}
\def\withdel@process#1{%
  \mathopen{\withdel@size\withdel@left}
  #1
  \mathclose{\withdel@size\withdel@right}
  \endgroup
}
\def\withdel@getdelims#1{\withdel@@getdelims#1..\@nil}
\def\withdel@@getdelims#1#2#3\@nil{%
  \def\withdel@left{#1}%
  \def\withdel@right{#2}%
}
\makeatother

\begin{document}

$\withdel(){x}$

$\bigl(x\bigr)$

\medskip

$\withdel[\Big][]{\withdel\{.{x}}$

$\Bigl[\bigl\{x\Bigr]$

\medskip

$\withdel\lbrack\rbrack{x}$

$\bigl[x\bigr]$

\medskip

$\withdel<>{x}$

$\bigl\langle x\bigr\rangle$

\end{document}

Notes

  1. You can use . for a null delimiter
  2. If the optional argument is not used and brackets are wanted for the delimiters, use \lbrack and \rbrack as shown in the examples.

enter image description here

1
  • Thanks for your answer. There was very little in between yours, Andrew Stacey's and Sean Allred's! I chose Andrew's because it was the one I made use of in the end and because I appreciated his very detailed explanation.
    – Hammerite
    Commented Sep 2, 2013 at 18:31
5

You could do this with a simple conditional, testing if the first argument is empty (other thing is if this will produce good typographical results; for example, in the second code below there would reallly no need for increased size delimiters):

\documentclass{article}
\usepackage{amsmath}

\newcommand\mybig[3]{%
\if\detokenize{#1}\relax\relax
  \bigl\{#3\bigr\}
\else
  \bigl#1#3\bigr#2
\fi}

\begin{document}

$\mybig{\lvert}{\rvert}{\lvert x-y\rvert}$

$\mybig{}{}{\lvert x-y\rvert}$

\end{document}

enter image description here

1
  • Your example with the braces still uses all three arguments (i.e. it is of the form \mybig{}{}{...}). The point of the question was that I want to be able to write \mybig{}{...}, the same way I would write \mybig(){...}, and have it Just Work.
    – Hammerite
    Commented Sep 2, 2013 at 3:06

You must log in to answer this question.

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