4

I wanted to create a table of symbols that is easy to read in LaTeX, so something like:

\begin{tabular}{*4M}
     <    & \le     & \ll   & \lll    \\
     >    & \ge     & \gg   & \ggg    \\
     \ne  & \approx & \prec & \preceq \\
     \sim & \simeq  & \succ & \succeq \\
\end{tabular}

and that produces both the symbols and command sequences when TeXed, so something like:

enter image description here

My code below almost does what I want except that, as shown above, \ne has unwanted protection. Is there a straightforward way around this? I have tried:

  • using \detokenize (see below)
  • using \string (doesn't compile)
  • using \cs_to_str:N from LaTeX3
  • using fancyvrb

Does anyone have a better suggestion?

Here is the code the produces the output above:

\documentclass{amsart}
\usepackage[T1]{fontenc}
\usepackage{mathtools}

\usepackage{amssymb}% load the AMS symbols
\usepackage[verb]{collcell}
\newcommand\showSymbol[1]{\hbox to 8em{\texttt{\detokenize{#1}}}$#1$}
\usepackage{array}
\newcolumntype{M}{>{\collectcell\showSymbol}l<{\endcollectcell}}

\begin{document}

  \begin{tabular}{*4M}
       <    & \le     & \ll   & \lll    \\
       >    & \ge     & \gg   & \ggg    \\
       \ne  & \approx & \prec & \preceq \\
       \sim & \simeq  & \succ & \succeq \\
  \end{tabular}

\end{document}
0

2 Answers 2

3

The problem is that at the beginning of table cells, TeX is expanding tokens looking for \omit or \span (for \multicolumn and such), so it expands \ne to \protect\ne␣, and \protect is \relax which (is not expandable so) is left alone and only then the table template is inserted with \collectcell\showSymbol, but at this point \ne is already expanded.

There are several ways you can work around this, but they boil down to either:

  1. Look at the expanded \ne (or whatever command) and check that the first token is \protect and typeset the second in that case, or

  2. Add something to stop TeX from expanding \ne.

For option 1 you can define \showSymbol like this (MWE 1 below):

\makeatletter
\newcommand\showSymbol[1]{%
  \expandafter\ifx\@car#1\@nil\protect\@empty
    \expandafter\@showSymbol\expandafter{\@cdr#1\@nil}%
  \else
    \@showSymbol{#1}%
  \fi}
\newcommand\@showSymbol[1]{%
  \hbox to 8em{\texttt{\detokenize{#1}}\hss}$#1$}
\makeatother

The line \expandafter\ifx\@car#1\@nil\protect\@empty will \ifx-compare the first token of #1 with with \protect (the \@empty is there for safety), and if so pass the remaining tokens to \@showSymbol. Note that this will pass \ne␣ rather than \ne to be typeset, so you might need to trim the trailing space.

For option 2 (MWE 2 below), quick and dirty, you can prefix \ne with \noexpand. When TeX expands \noexpand, it will temporarily do \let\ne\relax, and then the newly-redefined \ne will stop the expansion (and \ne will be restored to its original meaning). Then, when \collectcell does its job, \ne will be there, safe and sound:

\begin{tabular}{*4M}
  \noexpand\ne & \approx & \prec & \preceq \\
  <            & \le     & \ll   & \lll    \\
  >            & \ge     & \gg   & \ggg    \\
  \sim         & \simeq  & \succ & \succeq \\
\end{tabular}

MWE 1:

\documentclass{amsart}
\usepackage[T1]{fontenc}
\usepackage{mathtools}

\usepackage{amssymb}% load the AMS symbols
\usepackage[verb]{collcell}

\makeatletter
\newcommand\showSymbol[1]{%
  \expandafter\ifx\@car#1\@nil\protect\@empty
    \expandafter\@showSymbol\expandafter{\@cdr#1\@nil}%
  \else
    \@showSymbol{#1}%
  \fi}
\newcommand\@showSymbol[1]{%
  \hbox to 8em{\texttt{\detokenize{#1}}\hss}$#1$}
\makeatother

\usepackage{array}
\newcolumntype{M}{>{\collectcell\showSymbol}l<{\endcollectcell}}

\begin{document}

\begin{tabular}{*4M}
  \ne  & \approx & \prec & \preceq \\
  <    & \le     & \ll   & \lll    \\
  >    & \ge     & \gg   & \ggg    \\
  \sim & \simeq  & \succ & \succeq \\
\end{tabular}

\end{document}

MWE 2:

\documentclass{amsart}
\usepackage[T1]{fontenc}
\usepackage{mathtools}

\usepackage{amssymb}% load the AMS symbols
\usepackage[verb]{collcell}
\newcommand\showSymbol[1]{\hbox to 8em{\texttt{\detokenize{#1}}\hss}$#1$}
\usepackage{array}
\newcolumntype{M}{>{\collectcell\showSymbol}l<{\endcollectcell}}

\begin{document}

\begin{tabular}{*4M}
  \noexpand\ne & \approx & \prec & \preceq \\
  <            & \le     & \ll   & \lll    \\
  >            & \ge     & \gg   & \ggg    \\
  \sim         & \simeq  & \succ & \succeq \\
\end{tabular}

\end{document}
5
  • 2
    \robustify\ne?
    – Joseph Wright
    Jul 13, 2020 at 15:02
  • 2
    @JosephWright I didn't want to raise an existential issue for \protect ;-) Jul 13, 2020 at 15:05
  • Excellent! Thank you very much! I decided to use the \noexpand version, with the \noexpand placed at the end of the proceeding line to that it is "out of the way".
    – user30471
    Jul 13, 2020 at 23:51
  • 1
    @Andrew You can also do \let\n\noexpand and use \n, if you promise not to show that to anyone else ;-) Jul 13, 2020 at 23:56
  • I'm using this to show students LaTex commands, so I want to minimise the use of technicalities and should not teach them bad habits:) To this end I've switched back to using \string instead of \detokenize as this looks less cryptic.
    – user30471
    Jul 14, 2020 at 0:07
1

I'd use a different approach, namely to just list the symbols we want to make a table of and let TeX do the splitting.

The trailing optional argument (default 4) is for the number of pairs of columns; the first optional argument is for a title.

\documentclass{article}
\usepackage{amsmath,amssymb,booktabs}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\symboltable}{O{}mO{4}}
 {
  \andrew_symboltable:nnn { #1 } { #2 } { #3 }
 }

\tl_new:N \l__andrew_symboltable_tl
\seq_new:N \l__andrew_symboltable_seq

\cs_new_protected:Nn \andrew_symboltable:nnn
 {
  \group_begin:
  \setlength{\tabcolsep}{1pc}
  \seq_set_from_clist:Nn \l__andrew_symboltable_seq { #2 }
  \tl_if_blank:nTF { #1 }
   {
    \tl_clear:N \l__andrew_symboltable_tl
   }
   {
    \tl_set:Nn \l__andrew_symboltable_tl
     {
      \multicolumn{\int_eval:n { 2*(#3) } } { @{}c@{} } { #1 } \\ \midrule
     }
   }
  \seq_map_indexed_inline:Nn \l__andrew_symboltable_seq
   {
    \tl_put_right:Nn \l__andrew_symboltable_tl
     {
      \texttt{ \token_to_str:N ##2 } & $##2$
     }
    \int_compare:nTF { \int_mod:nn { ##1 } { #3 } = 0 }
     {
      \tl_put_right:Nn \l__andrew_symboltable_tl { \\ }
     }
     {
      \tl_put_right:Nn \l__andrew_symboltable_tl { & }
     }
   }
   \int_compare:nF { \int_mod:nn { \seq_count:N \l__andrew_symboltable_seq } { #3 } = 0 }
    {
     \tl_put_right:Nn \l__andrew_symboltable_tl { \\ }
    }
  \begin{tabular}{ @{} *{ #3 }{ l@{\hspace{6pt}}l } @{} }
  \toprule
  \tl_use:N \l__andrew_symboltable_tl
  \bottomrule
  \end{tabular}
  \group_end:
 }

\ExplSyntaxOff

\begin{document}

\begin{center}
\symboltable{
  <,\le,\ll,\lll,>,\ge,\gg,\ggg,\ne,\approx,
  \prec,\preceq,\sim,\simeq,\succ,\succeq
}

\bigskip

\symboltable[Relation symbols]{
  <,\le,\ll,\lll,>,\ge,\gg,\ggg,\ne,\approx,
  \prec,\preceq,\sim,\simeq,\succ,\succeq
}[5]
\end{center}

\end{document}

enter image description here

2
  • +1 This is nice, and clearly a "better" solution on some level, but it doesn't meet my first aim of being "easy to read in LaTeX" (In my use-case I'm not able to hide the code in a style file, but nor do I want to). The intended audience is familiar with tabular environments and the array package, which is why took the approach above. At this stage, sadly, LaTeX3 is a step too far.
    – user30471
    Jul 14, 2020 at 11:44
  • @Andrew I'm not sure that exposing the code to create such tables is good for beginners anyway.
    – egreg
    Jul 14, 2020 at 11:45

You must log in to answer this question.