8

I have created the following macros in LaTeX:

\def\twomate(#1,#2,#3,#4){\begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}}
\def\pwr(#1){^{#1}}
\def\twoclmn(#1,#2){\begin{pmatrix}#1\\#2\end{pmatrix}}

Where \twomate is a 2x2 matrix, \twoclmn creates a 2x1 column vector, and \pwr is a command to raise the argument to the power. However, if I try to do the following:

$$\twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),-2e\pwr(2t) )$$

I get an error. However, if I replace the last entry with something that is not a def command:

$$\twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),5 )$$

The document complies successfully. In addition, the following does not compile successfully:

$$\twoclmn(\frac{6}{25}e\pwr(2t)-\frac15te\pwr(2t),-\frac{2}{25}e\pwr(2t) + \frac25 t e\pwr(2t))$$

But the following does:

$$\twoclmn(\frac{6}{25}e^{2t}-\frac15te^{2t},-\frac{2}{25}e^{2t} + \frac25 t e^{2t})$$

So the problem seems to be the nested \def commands. How may I nest \def commands, without the compiler complaining?

  • Since you're using LaTeX, please consider using \[ ... \] instead of $$ ... $$, as the latter causes some inconsistencies. – AJFarmar Nov 10 '18 at 13:14
12

You can use xparse for this job, because the r argument type takes care of nesting.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\NewDocumentCommand{\twomate}{>{\SplitArgument{3}{,}}r()}{%
  \maketwomate#1%
}
\NewDocumentCommand{\maketwomate}{mmmm}{%
  \begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}%
}
\NewDocumentCommand{\pwr}{r()}{^{#1}}

\begin{document}

\[
\twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),-2e\pwr(2t) )
\]

\end{document}

However, using () as delimiters doesn't seem a good idea. The following is as clear and behaves better with syntax coloring of front ends.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\NewDocumentCommand{\twomate}{>{\SplitArgument{3}{,}}m}{%
  \maketwomate#1%
}
\NewDocumentCommand{\maketwomate}{mmmm}{%
  \begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}%
}
\NewDocumentCommand{\pwr}{m}{^{#1}}

\begin{document}

\[
\twomate{3e\pwr{-3t},e\pwr{2t},-e\pwr{-3t},-2e\pwr{2t}}
\]

\end{document}

enter image description here

  • can one nest like this \foo(a,\bar(b,c,d),\baz(e,f),g) ? – user4686 Nov 10 '18 at 16:11
  • @jfbu Yes, but why? There's no advantage whatsoever over braces. – egreg Nov 10 '18 at 16:13
  • actually I now tried and it does not seem to work with the code you posted (LaTeX error: "xparse/split-excess-tokens"). So I guess you meant "yes it can be done", not "yes, it does it". Again I agree that TeX has its own syntax and trying to use another one (with all induced complications) is not the way to go for user macros. – user4686 Nov 10 '18 at 16:50
  • @jfbu Without seeing how you define \foo, \bar and \baz it's just guesswork. – egreg Nov 10 '18 at 16:51
  • \twomate(\twomate(1,2,3,4),5,6,7), with \NewDocumentCommand{\maketwomate}{mmmm}{(#1+#2+#3+#4)} in place of the definition in your code – user4686 Nov 10 '18 at 16:52
10

Your \def contains a very specific sequence defined as the parameter text:

%           1   2   3   4   5
\def\twomate(<1>,<2>,<3>,<4>){ ... }
%           ^   ^   ^   ^   ^
%           │   │   │   │   │
%           │   └ comma ┘   │
%           └─── bracket ───┘

This parameter text is matched exactly in order (almost like a first-come-first-served style) to extract the four arguments <1>, <2>, <3> and <4>. Here's how the elements are grabbed for \twomate with the above notation:

%       1           2         3           4          5
\twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),-2e\pwr(2t))
%       ^           ^         ^           ^          ^
%       │           │         │           │          │
%       │           └────── comma ────────┘          │
%       └───────────────── bracket ──────────────────┘

It should be clear that the last bracket ) isn't properly captured for \pwr. The way around it is to hide \pwr(.) from \twomate:

\documentclass{article}

\usepackage{amsmath}

\def\twomate(#1,#2,#3,#4){\begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}}
\def\pwr(#1){^{#1}}

\begin{document}

\[
  \twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),{-2e\pwr(2t)})
\]

\end{document}

This solves the problem only temporarily. If you nest elements, you'll run into similar problems because of the parameter text pattern matching. In general, it is safer to group arguments using {...}.

6

TeX's delimited parameters parsing doesn't take nesting into account properly. So if you call \twomate(3e\pwr(-3t), ...), the final ) for \twomate isn't found at the end of that line but at the end of \pwr(-3t). The improperly formed call of \pwr then causes trouble.

To hide nested calls of your commands, put them into { ... } groups (though this probably defeats the purpose):

\documentclass{article}
\usepackage{amsmath}

\def\twomate(#1,#2,#3,#4){\begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}}
\def\pwr(#1){^{#1}}
\def\twoclmn(#1,#2){\begin{pmatrix}#1\\#2\end{pmatrix}}

\begin{document}
\[ \twomate({3e\pwr(-3t)},{e\pwr(2t)},{-e\pwr(-3t)},{-2e\pwr(2t)}) \]
\end{document}

enter image description here

By the way, don't use $$ ... $$ for display math environments but LaTeX's or amsmath's variants like \[ ... \].

2

Here is how to define macros expecting comma separated arguments enclosed in parentheses. The code converts any standard LaTeX command (macro with non-delimited parameters) into one expecting (..., ..., ...) input syntax. When used, the number of arguments must be the one expected by the macro, there is no error-checking.

Nesting works, as we will test with this kind of input:

\foo(\Bar(3,5,7),\Bar(9,\foo(a,\Bar(W,X,Y),c,d),13),\Bar(15,17,19),\Bar(21,23,25))

Attention that there is no special handling of spaces in the input, and also some brace-stripping may occur in certain circumstances.

As this all works purely by expansion, expandable commands remain expandable commands.

Important: for a fuller implementation of the analogy with "functional notation", the macro \foo(\a, \b, \c) should first expand completely the \a, \b, \c before doing whatever it wants to do (if only for reasons of efficiency). But this would limit from the start such constructs to the realm of expandable macros. And furthermore, only the new \expanded primitive allows (easy) such complete expansion. Else, one can make do restricting to only "first-fully" expandable macros using some \romannumeral-`Q governed expansion (with a problem if macro expands to something starting with a space token.) Anyway, here I did not add the automatic expansion of \a, \b, \c, before applying the \foo macro, I stayed more on the side of usual macro expansion in TeX for things such as \foo{\a}{\b}{\c}.

\documentclass{article}
\usepackage{amsmath}

% copy over some utility code from package xintexpr
\catcode`_ 11

\makeatletter
\let\xint_c_mone\m@ne
\let\xint_c_\z@
\let\xint_c_i\@ne
\long\def\xint_bye #1\xint_bye {}%
\makeatother
% THE \XINT_isbalanced_... MACROS COPIED OVER FROM XINTEXPR CODE MADE
% HERE ALL \long
% %    \end{macrocode}
% \subsubsection{\csh{XINT_isbalanced_a} for \cshnolabel{XINT_expr_onliteral_seq_a}}
% \lverb|Expands to \xint_c_mone in case a closing ) had no opening ( matching
% it, to \@ne if opening ( had no closing ) matching it, to \z@ if expression
% was balanced.|
%    \begin{macrocode}
% use as \XINT_isbalanced_a \relax #1(\xint_bye)\xint_bye
\long\def\XINT_isbalanced_a #1({\XINT_isbalanced_b #1)\xint_bye }%
\long\def\XINT_isbalanced_b #1)#2%
   {\xint_bye #2\XINT_isbalanced_c\xint_bye\XINT_isbalanced_error }%
%    \end{macrocode}
% \lverb|if #2 is not \xint_bye, a ) was found, but there was no (. Hence error -> -1|
%    \begin{macrocode}
\long\def\XINT_isbalanced_error #1)\xint_bye {\xint_c_mone}%
%    \end{macrocode}
% \lverb|#2 was \xint_bye, was there a ) in original #1?|
%    \begin{macrocode}
\long\def\XINT_isbalanced_c\xint_bye\XINT_isbalanced_error #1%
    {\xint_bye #1\XINT_isbalanced_yes\xint_bye\XINT_isbalanced_d #1}%
%    \end{macrocode}
% \lverb|#1 is \xint_bye, there was never ( nor ) in original #1, hence OK.|
%    \begin{macrocode}
\long\def\XINT_isbalanced_yes\xint_bye\XINT_isbalanced_d\xint_bye )\xint_bye {\xint_c_ }%
%    \end{macrocode}
% \lverb|#1 is not \xint_bye, there was indeed a ( in original #1. We check if
% we see a ). If we do, we then loop until no ( nor ) is to be found.|
%    \begin{macrocode}
\long\def\XINT_isbalanced_d #1)#2%
   {\xint_bye #2\XINT_isbalanced_no\xint_bye\XINT_isbalanced_a #1#2}%
%    \end{macrocode}
% \lverb|#2 was \xint_bye, we did not find a closing ) in original #1. Error.|
%    \begin{macrocode}
\long\def\XINT_isbalanced_no\xint_bye #1\xint_bye\xint_bye {\xint_c_i }%

% NEW EXPANDABLE UTILITY

\long\def\applyfunction #1#2)%
{%
    \ifcase\XINT_isbalanced_a \relax #1#2)(\xint_bye)\xint_bye
           \expandafter\applyfunction_b
        \or\expandafter\applyfunction_again
      \else\expandafter\we_are_doomed
    \fi {#1#2}%
}%
\long\def\applyfunction_again #1{\applyfunction {#1)}}%
\long\def\my_bbye #1\my_bbye {}%
\long\def\applyfunction_b #1{\applyfunction_c #1,\my_bbye,}%
\long\def\applyfunction_c #1({\applyfunction_d #1{}{}}%
% we will take care of brace removal another day
\long\def\applyfunction_d #1#2#3#4,%
{%
    \ifcase\XINT_isbalanced_a \relax #3#4(\xint_bye)\xint_bye
           \expandafter\applyfunction_e
        \or\expandafter\applyfunction_d_again
      \else\expandafter\we_are_doomed
    \fi #1{#2}{#3}{#4}%
}%
\long\def\applyfunction_d_again #1#2#3#4%
{%
    \applyfunction_d #1{#2}{#3#4,}%
}%
\long\def\applyfunction_e #1#2#3#4%
{%
    \my_bbye#4\applyfunction_finish\my_bbye
    \applyfunction_g #1{#2}{#3#4}%
}%
\long\def\applyfunction_g #1#2#3{\applyfunction_d #1{#2{#3}}{}}%
\long\def\applyfunction_finish\my_bbye\applyfunction_g #1#2#3{#1#2}%

\catcode`_ 8

%% USAGE
% to define a "function" to be used as \foo(a,b,c,...),
% we need a \foomacro which uses standard non-delimited parameters,
% thus e.g. defined via \newcommand, and then one only needs
% \newcommand\foo{\applyfunction\foomacro}
% to get a \foo to be used with parentheses.

\newcommand\twomate{\applyfunction\twomatemacro}
\newcommand\pwr{\applyfunction\pwrmacro}
\newcommand\twoclmn{\applyfunction\twoclmnmacro}

% define here the **non-delimited** auxiliary macros
\newcommand\twomatemacro[4]{\begin{pmatrix}#1&#2\\#3&#4\end{pmatrix}}
\newcommand\pwrmacro[1]{^{#1}}
\newcommand\twoclmnmacro[2]{\begin{pmatrix}#1\\#2\end{pmatrix}}

%% TESTING NESTING
\newcommand\foo{\applyfunction\foomacro}
\newcommand\foomacro[4]{\left[#1+#2+#3+#4\right]}
\let\Bar\relax
\newcommand\Bar{\applyfunction\Barmacro}
\newcommand\Barmacro[3]{\left(#1*#2*#3\right)}

\delimiterfactor1001

\begin{document}

\[
  \twomate(3e\pwr(-3t),e\pwr(2t),-e\pwr(-3t),-2e\pwr(2t))
\]
\[
  \twoclmn(\frac{6}{25}e\pwr(2t)-\frac15te\pwr(2t),-\frac{2}{25}e\pwr(2t)
  + \frac25 t e\pwr(2t))
\]
\[\foo(\Bar(3,5,7),\Bar(9,\foo(a,\Bar(W,X,Y),c,d),13),\Bar(15,17,19),\Bar(21,23,25))\]
\end{document}

enter image description here

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.