[Tex/LaTex] Passing an argument with the alignment operator (&) to a macro within another aligned environment

alignmacrosmath-modematrices

I am trying to write macros for quickly formatting matrices in math mode. Here is a simple, working example:

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{displaymath}
M = \pmat{1&0\\0&1}
\end{displaymath}
\end{document}

This also works if used within an aligned environment (a table, align environment, cases environment, etc.):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{align}
M &= \pmat{1&0\\0&1}
\end{align}
\end{document}

Normally, within the align environment the & symbol tells LaTeX to start a new column, but since the matrix values are contained between {} group tokens, LaTeX does not attempt to parse the 1&0\\0&1 argument and thus everything works as it was intended.

Let's create a new macro that does not use the group tokens {} to denote the macro argument. For example, it is fairly common to use square brackets to group an argument as in this working example (try it!):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{displaymath}
M = \bmat[1&0\\0&1]
\end{displaymath}
\end{document}

So far, so good. Here comes the problem! If I try to use my new macro \bmat within another aligned environment:

% DOES NOT WORK!!!
\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}

I get an error because LaTeX tries to interpret the & within the matrix argument as part of the outer align environment.

The difference between the way LaTeX treats the first macro \pmat{} and the second one \bmat[] is that the group tokens {} prevent LaTeX from parsing the argument contained within the curly braces until after the macro \pmat has been called. On the other hand, when LaTeX sees the \bmat[arg] macro, the argument is not surrounded by group tokens and so LaTeX tries to parse it before getting eaten up by \bmat. The extra & character screws up the syntax of the align environment and so LaTeX crashes. At least, that is my understanding of what is going on, and I could be wrong!

So, the question is whether or not it is possible to force LaTeX to treat the argument in the [] case just as it did in the {} case (without causing additional problems). In other words, I want to be able to write a macro a-la \bmat[1&0\\0&1] that works in aligned environments but retains the square bracket syntax.

CONCLUSION:

Here is the final macro with working [] syntax, thanks to egreg (requires xparse and must be contained in a separate package file or between \makeatletter and \makeatother in preamble):

\DeclareDocumentCommand\mat{}{{\ifnum\z@=`}\fi\@mat}
\DeclareDocumentCommand\@mat{ g o d() d|| }
{
    \IfNoValueTF{#1}
    {
        \IfNoValueTF{#2}
        {
            \IfNoValueTF{#3}
            {
                \IfNoValueTF{#4}
                {()}
                {\begin{vmatrix}#4\end{vmatrix}}
            }
            {
                \begin{pmatrix}#3\end{pmatrix}
                \IfNoValueTF{#4}{}{|#4|}
            }
        }
        {
            \begin{bmatrix}#2\end{bmatrix}
            \IfNoValueTF{#3}{}{(#3)}
            \IfNoValueTF{#4}{}{|#4|}
        }
    }
    {
        \begin{matrix}#1\end{matrix}
        \IfNoValueTF{#2}{}{[#2]}
        \IfNoValueTF{#3}{}{(#3)}
        \IfNoValueTF{#4}{}{|#4|}
    }
    \ifnum\z@=`{\fi}
}

This macro works like an overloaded function with different argument delimiters {}, [], (), or ||. There are probably many reasons why this is a terrible idea — so please, post your comments below. I would like to hear from those who know better than me.

Best Answer

You can do it, but I strongly recommend you not doing it. You gain nothing in using [ and ] as delimiters for a mandatory argument.

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand\bmat{{\ifnum\z@=`}\fi\@bmat}
\def\@bmat[#1]{\begin{bmatrix}#1\end{bmatrix}\ifnum\z@=`{\fi}}
\makeatother

\begin{document}
$\bmat[1&0\\0&1]$
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}

Result from the example above

(Note: I have reduced the text width to shorten the output.)

When you understand the trick, you are allowed to use it. :) Hint: look for \eegroup in Appendix D of the TeXbook.


Defining \mat to look for the next token in order to decide a delimiter is, in my opinion, wrong. I'd do like this:

\newcommand{\mat}[2][]{\begin{#1matrix}#2\end{#1matrix}}

so that

\mat{1&2\\3&4}    % no fences
\mat[b]{1&2\\3&4} % brackets
\mat[p]{1&2\\3&4} % parentheses
\mat[v]{1&2\\3&4} % vertical lines
\mat[V]{1&2\\3&4} % double vertical lines
\mat[B]{1&2\\3&4} % braces

would do even better, with minimum hassle and better readability.

Personally, I would stick with the long form, but it's a matter of taste.