6

On this thread, I found a piece of code that I found really interesting and useful, but I have no idea how it works: https://tex.stackexchange.com/a/584979/261033

Here's the code in question, for reference:

\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\matr#1{\def\matrL{}\matrA#1\end}
\def\matrA#1{\ifx\end#1\pmatrix{\matrL}\else 
   \ifx,#1\addto\matrL{&}\else
   \ifx;#1\addto\matrL{\cr}\else
   \addto\matrL{#1}\fi\fi
   \expandafter\matrA\fi
}

It can be called as \matr{a,b;c,d} and it generates a pmatrix. Very useful in my opinion if you're often working with simple matrices and are tired of repeatedly typing the full long command. Someone also pointed out that to make it work with the amsmath package, you need to replace the \pmatrix{\matrL} with \begin{pmatrix}\matrL\end{pmatrix}.

Anyways, I wish to make further modifications to this command, for example use different symbols as row/column separators, make the input parsing column-wise, or generalize it for all matrix bracket types (so you could type something like \matr{[)}{...} and it will generate a matrix with that specific set of enclosing brackets, akin to \left[\begin{matrix}...\end{matrix}\right)), but I don't know how to do any of that yet.

I tried to learn something about these commands, but quickly ran into issues. It seems that some of them, such as \addto, are so obscure, that there exists no documentation for them anywhere! Google could only find things like \addtocontents or \g@addto@macro, which I'm not interested in, and I also failed to find any comprehensive TeX resource that would list them, not even https://www.latex-project.org or https://texdoc.org/index.html.

So please, if anyone could help me with this, perhaps point me to some actual existing documentation for these commands, I would greatly appreciate it. Thank you in advance.

Tsskyx
  • 115
  • 1
    you show the definition of \addto on the first line you can not Google for the name it's just a local definition, you could change addto to abc and it would work the same way – David Carlisle Jan 17 '24 at 07:35
  • 1
    \addto is documented in OpTeX documentation. – wipet Jan 17 '24 at 11:09

2 Answers2

9

The \addto macro appends code to another macro.

If you have \def\foo{X} and do \addto\foo{Y}, TeX will do

\expandafter\def\expandafter\foo\expandafter{\foo Y}

which results in

\def\foo{XY}

after the \expandafter chain has done its work. However, you can't use \addto if you plan to use the code in LaTeX, because \addto is an important command used by babel and you risk disaster if you redefine it. Just use another name.

What does \matr do? It does \def\matrL{}, which initializes a container for the body of the matrix. Next it passes the argument to \matrA, starting a recursion.

The macro \matrA just read the following tokens one by one:

  • if the next token is , (denoting end of a cell), then & is appended to \matrL;
  • if the next token is ; (denoting end of a row), then \cr is appended to \matrL;
  • if the next token is \end, the endgame is run, namely \pmatrix{\matrL} is executed and the recursion stops;
  • in all other cases, the token is just appended to \matrL.

Can you adapt this to LaTeX and specifically to a pmatrix environment? Yes, easily.

First of all, choose different names for the internal macros, preferably with @ in them.

\documentclass{article}
\usepackage{amsmath}

\makeatletter \newcommand{\matr}[1]{\def\tsskyx@matrL{}\tsskyx@matrA#1\end}

\def\tsskyx@addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}

\def\tsskyx@matrA#1{% \ifx\end#1% \begin{pmatrix}\tsskyx@matrL\end{pmatrix}% \else \ifx,#1% \tsskyx@addto\tsskyx@matrL{&}% \else \ifx;#1% \tsskyx@addto\tsskyx@matrL{\}% \else \tsskyx@addto\tsskyx@matrL{#1}% \fi \fi \expandafter\tsskyx@matrA \fi } \makeatother

\begin{document}

[ \matr{a,b;c,d} ]

\end{document}

The same code, with minimal differences: instead of \cr we append \\ and \begin{pmatrix}...\end{pmatrix} is used.

A different implementation using expl3, where an optional argument specifies the delimiters (default p, can be bvVB with amsmath conventions).

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\matr}{O{p}m} { \tsskyx_matr:nn { #1 } { #2 } }

\seq_new:N \l__tsskyx_matr_body_seq

\cs_new_protected:Nn \tsskyx_matr:nn { % build the matrix \begin{#1matrix} % split the argument at ; \seq_set_split:Nnn \l__tsskyx_matr_body_seq { ; } { #2 } % map the items, that are comma separated lists \seq_map_function:NN \l__tsskyx_matr_body_seq __tsskyx_matr_row:N \end{#1matrix} }

\cs_new_protected:Nn __tsskyx_matr_row:N { \clist_use:nn { #1 } { & } \ }

\ExplSyntaxOff

\begin{document}

[ \matr{a,b;c,d} \qquad \matr[b]{a,b;c,d} ]

\end{document}

enter image description here

If you also want to change the row and column separator, you can use a key-value system.

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\matr}{O{}m} { \group_begin: \keys_set:nn { tsskyx/matr } { #1 } \tsskyx_matr:n { #2 } \group_end: }

\seq_new:N \l__tsskyx_matr_body_seq \seq_new:N \l__tsskyx_matr_row_seq

\keys_define:nn { tsskyx/matr } { row-sep .tl_set:N = \l__tsskyx_matr_rowsep_tl, col-sep .tl_set:N = \l__tsskyx_matr_colsep_tl, delim .tl_set:N = \l__tsskyx_matr_delim_tl, row-sep .initial:n = { ; }, col-sep .initial:n = { , }, delim .initial:n = { p }, }

\cs_new_protected:Nn \tsskyx_matr:n { % build the matrix \begin{\l__tsskyx_matr_delim_tl matrix} % split the argument at ; \seq_set_split:NVn \l__tsskyx_matr_body_seq \l__tsskyx_matr_rowsep_tl { #1 } % map the items, that are comma separated lists \seq_map_function:NN \l__tsskyx_matr_body_seq __tsskyx_matr_row:N \end{\l__tsskyx_matr_delim_tl matrix} }

\cs_new_protected:Nn __tsskyx_matr_row:N { \seq_set_split:NVn \l__tsskyx_matr_row_seq \l__tsskyx_matr_colsep_tl { #1 } \seq_use:Nn \l__tsskyx_matr_row_seq { & } \ }

\ExplSyntaxOff

\begin{document}

\begin{gather} \matr{a,b;c,d} \ \matr[delim=b]{a,b;c,d} \ \matr[row-sep=\,col-sep=&,delim=V]{a & b \ c & d} \ \matr[row-sep=;,col-sep=:]{a:b;c:d} \end{gather}

\end{document}

enter image description here

egreg
  • 1,121,712
  • 1
    Right, so addto was actually a newly defined macro. I appear to have entirely misunderstood that. – Tsskyx Jan 18 '24 at 06:50
6

The \addto macro mentioned in the code is from OPmac macros designed originally for csplain, later OPmac for plain TeX, nowadays in OpTeX. The author of these macro packages doesn't support LaTeX. His macros are primarily for plain TeX and because they are mostly based on TeX primitives, they are applicable in LaTeX too. But there are sometimes a little obstacles like the name \addto shared with the same name in a LaTeX package. Because the author doesn't use LaTeX or its packages (like babel) then it is no problem for him and for other plain TeX users.

If you want to modify the macro in order to enable to set the parenthesis type of the matrix in the parameter, then you can sent the given type into the definition of \matrA defined inside the \matr macro. So, you have \def in \def trick:

\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\matr#1#2#3{\def\matrL{}%
   \def\matrA##1{\ifx\end##1\left#1\matrix{\matrL}\right#2\else 
      \ifx,##1\addto\matrL{&}\else
      \ifx;##1\addto\matrL{\cr}\else
      \addto\matrL{##1}\fi\fi
      \expandafter\matrA\fi
   }
   \matrA#3\end
}

Test and usage: $$ \matr[]{a,b;c,d} $$

\bye

Edit For comparison with another answer here, I show how to use key-value macros from OpTeX:

\def\matrdefaults{%
   delims=(),   % delimiters of the matrix
   row-sep={;}, % row separator used in parameter
   col-sep={,}, % column separator used in parameter
}
\optdef\matr [] #1{{%
   \readkv\matrdefaults \readkv{\the\opt}%
   \ea\matrX \expanded{\kv{delims}\kv{row-sep}\kv{col-sep}}#1\end
}}
\def\matrX#1#2#3#4{\def\matrL{}%
   \def\matrA##1{\ifx\end##1\left#1\matrix{\matrL}\right#2\else 
      \ifx#4##1\addto\matrL{&}\else
      \ifx#3##1\addto\matrL{\cr}\else
      \addto\matrL{##1}\fi\fi
      \ea\matrA\fi
   }
   \matrA
}

$$ \displaylines{ \matr {a,b;c,d} \cr \matr [delims={[]}] {a,b;c,d} \cr \matr [row-sep={\cr}, col-sep={&}, delims={||}] {a&b\cr c&d} \cr \matr [row-sep=;,col-sep=:]{a:b;c:d} } $$

\bye

And if you want to create your package with this macro, then you should use a separate name space (tsskyx here). The control sequences in this namespace are prefixed by . and other used control sequences (primitives and OpTeX macros) are prefixed by _. Then your macro is isolated from user name space (unprefixed names) and from other macro packages with different name space.

\_namespace{tsskyx}
\_def\.matrdefaults{%
   delims=(),   % delimiters of the matrix
   row-sep={;}, % row separator used in parameter
   col-sep={,}, % column separator used in parameter
}
\_optdef\.matr [] #1{{%
   \_kvdict{tsskyx}%
   \_readkv\.matrdefaults \_readkv{\_the\_opt}%
   \_ea\.matrX \_expanded{\_kv{delims}\_kv{row-sep}\_kv{col-sep}}#1\_end
}}
\_def\.matrX#1#2#3#4{\_def\.matrL{}%
   \_def\.matrA##1{\_ifx\_end##1\_left#1\_matrix{\.matrL}\_right#2\_else 
      \_ifx#4##1\_addto\.matrL{&}\_else
      \_ifx#3##1\_addto\.matrL{\_cr}\_else
      \_addto\.matrL{##1}\_fi\_fi
      \_ea\.matrA\_fi
   }
   \.matrA
}
\_nspublic \matr ;
\_endnamespace

$$ \displaylines{ \matr {a,b;c,d} \cr \matr [delims={[]}] {a,b;c,d} \cr \matr [row-sep={\cr}, col-sep={&}, delims={||}] {a&b\cr c&d} \cr \matr [row-sep=;,col-sep=:]{a:b;c:d} } $$

\bye

wipet
  • 74,238