12

This macro is quite old, and I don't know plain TeX very well. MWE:

\documentclass{article}

\setlength{\unitlength}{0.06em}
\newlength{\cellsize} \setlength{\cellsize}{18\unitlength}
\newsavebox{\cell}
\sbox{\cell}{\begin{picture}(18,18)
\put(0,0){\line(1,0){18}}
\put(0,0){\line(0,1){18}}
\put(18,0){\line(0,1){18}}
\put(0,18){\line(1,0){18}}
\end{picture}}
\newcommand\cellify[1]{\def\thearg{#1}\def\nothing{}%
\ifx\thearg\nothing
\vrule width0pt height\cellsize depth0pt\else
\hbox to 0pt{\usebox{\cell} \hss}\fi%
\vbox to \cellsize{
\vss
\hbox to \cellsize{\hss$#1$\hss}
\vss}}
\newcommand\tableau[1]{\vtop{\let\\\cr
\baselineskip -16000pt \lineskiplimit 16000pt \lineskip 0pt
\ialign{&\cellify{##}\cr#1\crcr}}}

\begin{document}
  \tableau{ & & & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}
\end{document}

Output.

As for my confusion, it's mostly coming from

\ialign{&\cellify{##}\cr#1\crcr}}}

I know a double hash inside a nested macro definition expands to a single hash and allows the inner macro to take arguments, but that doesn't seem to be what's going on here. I have no idea what a single hash alone means. I tried searching but was only able to find explanations for appearances of eg. ##1 rather than ## or just #. It seems to somehow be breaking up the #1 input at alignment characters and passing them to cellify, but the specifics are black magic to me.

The reason I'm wondering is that I was trying to extend the macro to use a different picture (dashed lines) if a certain character was used, but just adding an extra \if branch inside \cellify comparing #1 to e.g. - didn't work. Well, it did if you called \cellify on its own, but \tableau seems to be giving it more tokens.

I have very little plain TeX experience and am also confused by quite a few other things in this macro that don't seem to be documented well online (\ialign; \cr; \crcr; \hss; \vss). Any explanation would be appreciated. Thanks.

12

## in a macro definition puts # in the replacement text (which is why ##1 puts the required #1 in a nested definition).

So if the argument of \cellify is X then when that is expanded, #1 is replaced by X and ## is replaced by #

so

\ialign{&\cellify{##}\cr#1\crcr}

expands to

\ialign{&\cellify{#}\cr X\crcr}

\ialign is a command defined in plain TeX (and also inherited in latex) which uses a syntax closer to the primitive \halign than the latex tabular but it is essentially doing the same thing.

The first row (up to the first \cr is the alignment template. here it begins with & which is a special syntax that means any number of columns are allowed, and each cell gets the code \cellify{...} where ... s the cell content between &.

In this example there is only one cell in the table and that would be typeset as

\cellify{X}

Here is a version that detects a cell with just - and makes a cell with a ! just as a demo.

\documentclass{article}

\setlength{\unitlength}{0.06em}
\newlength{\cellsize} \setlength{\cellsize}{18\unitlength}
\newsavebox{\cell}
\sbox{\cell}{\begin{picture}(18,18)
\put(0,0){\line(1,0){18}}
\put(0,0){\line(0,1){18}}
\put(18,0){\line(0,1){18}}
\put(0,18){\line(1,0){18}}
\end{picture}}
\def\dashtest{-}
\newcommand\cellify[1]{\def\thearg{#1}\def\nothing{}%
\ifx\thearg\nothing
\vrule width0pt height\cellsize depth0pt\else
\ifx\thearg\dashtest
\hbox to 0pt{\usebox{\cell}\kern-2pt!\hss}%or whetever
\def\thearg{}%
\else
\hbox to 0pt{\usebox{\cell} \hss}\fi\fi
\vbox to \cellsize{
\vss
\hbox to \cellsize{\hss$\thearg$\hss}
\vss}}
\newcommand\tableau[1]{\vtop{\let\\\cr
\baselineskip -16000pt \lineskiplimit 16000pt \lineskip 0pt
\ialign{&\cellify{##}\cr#1\crcr}}}

\begin{document}
  \tableau{ & &-& 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}
\end{document}
  • Thank you for the explanation. I've asked one further thing in a response to egreg's answer, clarifying why my naive extension of \tableau didn't work. I'm sure it's simple if you know the system. – Josh Swanson Feb 23 '15 at 23:38
  • \hbox to \cellsize{\hss$a #1 b$\hss} doesn't work as \hbox to \cellsize is the primitive version of \makebox[\cellsize]{...} so the width of the cell is forced to be exactly \cellsize and if you put more stuff in then it overprints to the left and right – David Carlisle Feb 23 '15 at 23:44
  • @JoshSwanson see updated answer – David Carlisle Feb 23 '15 at 23:55
8

Let's start from the main part:

\ialign{&\cellify{##}\cr#1\crcr}

As usual, #1 will be replaced by the argument given at call time, so it's not very relevant. The macro \ialign is defined in Plain TeX as

\everycr{}\tabskip\z@skip\halign

so it's an “initialized” \halign, where some parameters are cleared. The important object is the primitive \halign, which is the basis all tables are built on.

The syntax of this primitive is

\halign{<templates>\cr<body of the table>}

where <body of the table> consists of the cells, separated by &, and the rows are terminated by \cr. Other material can go after \cr as argument to \noalign.

The <templates> are the specifications for each table column, again separated by &. A template consists of three parts, traditionally called u and v, and in between them a “placeholder” # should be inserted, which represents the cell's content in the table body. In our case we have a single template; the initial & means that the number of columns is unspecified and all of them will share the same template. Here

u = \cellify{
v = }

This means that after TeX has determined a cell's contents, it will substitute it with \cellify{<cell contents>}.

It's just that simple! A template is very similar to a one parameter macro, but instead of #1 just # is used.

Why doubling it? Because it's inside a definition, in the same spirit as in What is the meaning of double pound symbol (number sign, hash character) ##1 in an argument?

So, when \tableau is expanded, the correct single # will appear in the main input stream.

The primitive \crcr means: add \cr if and only if the argument #1 doesn't provide a closing \cr (that, by virtue of the instruction \let\\\cr will probably be \\). A closing \cr is needed for \halign to complete its work, and this trick allows the user not to specify a trailing \\ when using \tableau.

You can read about \hss and \vss in TeX by Topic (click on the link or type texdoc texbytopic in a terminal window) or in the TeXbook; the books have much more about \halign.

  • 1
    Thank you, that's so much clearer. It seems I was mostly missing "templates" in \halign and friends. One nagging question: \ialign is passing more than just the numbers in my tableau, eg. 5, 6, 8, to \cellify. For instance, if I replace \hss$#1$\hss with \hss$a #1 b$\hss I get weird spacing issues. What is it actually passing and how could I do my proposed extension of \tableau where eg. a - gives rise to a dotted box? (I can make the dotted box variant of \cell myself.) – Josh Swanson Feb 23 '15 at 23:34
  • @JoshSwanson Are you meaning “dotted borders”? The borders are drawn with \hrule and \vrule. – egreg Feb 23 '15 at 23:51
1

The macro discussed here is very curious mix of plain TeX and LaTeX approach. In pure plain TeX it can look like:

\def\tableau#1{\vbox{\offinterlineskip \let\\=\cr \ialign{&\cellify{##}\cr #1\crcr}}}
\def\cellify#1{\ifx^#1^\else
   \vbox{\kern-.2pt\hrule
         \hbox to18pt{\kern-.2pt\vrule height12pt depth6pt\hss#1\unskip\hss\vrule\kern-.2pt}
         \hrule \kern-.2pt}%
   \fi
}

\tableau{ & & & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}

\end

And if you need to change - to X (for example), then you can replace \hss#1\unskip by \hss\tableauA#1\unskip and you can define:

 \def\tableauA#1#2\unskip{\ifx-#1x\else#1#2\unskip\fi}

 \tableau{ & & - & 5 & 6 & 8\\ & & 1 & 2 & 3\\ 3 & 5 & 7 & 5}

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.