Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Writing (La)TeX code sometimes requires a degree of guile. Here (in no particular order) are two of my favourite examples.

Macros ending with spaces

Pete asked how to see the implementation of \LaTeX, since \show\LaTeX results in \protect \LaTeX. Joseph Wright's suggestion made my day. (I should probably try to get out more).

\let\protect\show
\LaTeX

Uppercasing a variable

I asked how to store the upper case counterpart of a string stored in a variable, because

\def\word{abc}
\def\WORD{\MakeUppercase{\word}}
\show\WORD

returns \MakeUppercase {abc}. Bruno Le Floch offered a superb solution.

\MakeUppercase{\gdef\noexpand\WORD{\word}}

What are your favourite cunning (La)TeX tricks?

Moderator note: this should probably be community wiki.

share|improve this question
3  
We should also warn of dangerous uses of primitives and tricks. The implications are not always clear to the neophyte (I too am a novice). I have seen a few dangerous assignments but the following is one type that worries me most: \let\num\m@ne A few commands later the programmer then did \global\advance\num2 This dangerously changes the meaning of \m@ne, as \showthe\m@ne shows. The assignment is dangerous enough without \global. –  Ahmed Musa Dec 15 '11 at 17:19
add comment

5 Answers

This is my killing two birds with one stone technique. Sorting a table and having markdown syntax without catcode changes.

First create a list and a helper macro to add elements.

\let\alist\@empty

\def\addtolist#1#2{%
  \lst@lAddTo#1{#2}}

Create two commands one to sort based on Column A, as a primary index or Column B.

\def\RA#1|#2|#3|#4;{%
  \addtolist{\alist}{#1#2,}%
  \expandafter\gdef\csname#1#2\endcsname{\textit{#1}&#2&#3&#4\cr\relax}
  \lst@BubbleSort{\alist}
}

The sorting macro is from the listings documentation class lstdoc. Data is added as shown below, using the semicolon as an end of line delimiter is a left over from my Pascal days, you can use space if your last column is just numbers or single words.

\RA Lactarius fallax      | velvety milk cap |edible |potentially risky;  
\RA Lactarius camphoratus | candy cap        |edible |aromatic qualities;

In the minimal everything is placed in a box, but it can equally work well using environments or more complicated macros. Change \RA to \RB to sort based on the second column as a primary index.

\documentclass{article}
\usepackage{lstdoc,booktabs}
\begin{document}
\makeatletter
\let\alist\@empty

\def\addtolist#1#2{%
  \lst@lAddTo#1{#2}
}

\def\RA#1|#2|#3|#4;{%
  \addtolist{\alist}{#1#2,}%
  \expandafter\gdef\csname#1#2\endcsname{\textit{#1}&#2&#3&#4\cr\relax}
  \lst@BubbleSort{\alist}
}

\def\RB#1|#2|#3|#4;{%
   \addtolist{\alist}{#2#1,}%
   \expandafter\gdef\csname#2#1\endcsname{\textit{#1}&#2&#3&#4\cr\relax}
   \lst@BubbleSort{\alist}
}

%% adding the data now
\RA Lactarius fallax      | velvety milk cap |edible |potentially risky;  
\RA Lactarius camphoratus | candy cap        |edible |aromatic qualities;
\RA Suillus pungens       | slippery Jack    |edible |poor taste;
\RA Lactarius affinis     | kindred milk     |edible |unpalatable;
\RA Calocybe carnea       | pink fairhead    |edible |potentially risky;
\RA Amateta ocreata       | death angel      |inedible |highly poisonous;   

%% typesetting the table
\newsavebox{\tempbox}
\savebox{\tempbox}{
\centering
\begin{tabular}{llll}
  \toprule[1pt]
  Species & Common name & Edibility & Remarks\\
  \midrule
  \@for\i:=\alist \do{\csname\i\endcsname}
  \vspace{-14pt}\\\bottomrule
\end{tabular}
}

\begin{table}
\usebox{\tempbox}
\caption{Some mushrooms from Wikipedia}
\end{table}
\end{document}

You can add more tricks, depending in what you want to achieve, slightly changing the definition of \RA and \RB you can create automatic links to Wikipedia articles or insert images. This is much quicker than using DBtools and much more flexible.

share|improve this answer
add comment
  1. A very useful one is that \romannumeral expands everything on its way until it finds an integer. If the integer is negative, it expands to nothing. This can be used to fully expand something as follows:

    \def\a{\b}\def\b{\c}\def\c{\d}\def\d{1}
    \expandafter\show\romannumeral-`\0\a
    

    Here -`0 is not a complete number (it needs a space to become minus the character code of 0), so TeX looks ahead, expanding tokens as it goes. The code will show 1. On the other hand, \expandafter\show\romannumeral0\a would lead to some surprises: after a few expansions, \a yields 1, and TeX is now seeing \romannumeral01, which will expand to i (or something else if more digits lie ahead). (Thanks egreg for the comment.)

  2. I also like the combination of \afterassignment and \futurelet to read two tokens ahead. For instance,

    \newcommand{\readhead} [2]
      {\afterassignment\@gobble\futurelet#1{#2}}
    

    will set #1 equal to the first token of the list of tokens #2, then remove #2 from the input stream.

  3. Testing if a given token is either -, +, a digit, or something else:

    \newcommand{\test}[1]{%
      \ifcase\numexpr1\noexpand#11\relax
            % -
      \or   % other
      \or   % +
      \else % digit
      \fi}
    

    Replace \noexpand by \string if the goal is to test the character code of a character instead (regardless of category code), assuming that \escapechar is not too crazy.

  4. Test if a character is a letter.

    \ifnum\numexpr\uccode`#1/26=3 True\else False\fi
    

    Of course this breaks if someone plays around with uppercase codes.

  5. Test if a character is a digit.

    \ifnum 9<1#1 True\else False\fi
    
share|improve this answer
    
So in the first case, does that mean that \romannumeral-`\0 will keep expanding until it hits a space token? –  Andrew Stacey Jun 3 '11 at 10:13
1  
@Andrew Until an unexpandable token is found; if this token is a space token, it's ignored. –  egreg Jun 3 '11 at 10:48
    
@Bruno Perhaps you can add why -\0 is used instead of a simple 0`: in your example if \d expands to 1, with your construction 1 is found, with \romannumeral0 a surprise would hit the user. –  egreg Jun 3 '11 at 10:50
    
@egreg: Interesting. I wonder if that would supply a possible answer to my question: tex.stackexchange.com/q/332/86 –  Andrew Stacey Jun 3 '11 at 10:52
5  
I just want to say that I find all of these appalling but fascinating, like quicksand or certain Australian spiders. –  Ryan Reich Nov 4 '11 at 17:19
show 6 more comments

I like all the catcode changes (do not understand them half the time :-). The following is a very simple function to typeset SI units where a . is made active in math mode and typed as \cdot

{\catcode`\.=13 \gdef.{{\cdot}}}
\newcommand*\unit[1]{%
    \begingroup%
        \mathcode`.="8000%
        \ensuremath{\mathrm{#1}}%
    \endgroup}
\newcommand*\SI[2]{\ensuremath{#1\,\unit{#2}}}

Then the code

\noindent$\unit{N}=\unit{kg.m.s^{-2}}$\qquad \SI{1.2\times10^2}{N.m}

gives

enter image description here

Another great math catcode one is code posted by the late Michael J Downes that will turn -> into shorthand for \rightarrow in math mode.

\makeatletter
\mathchardef\mathminus=\mathcode`\-
\begingroup\catcode`\-=13 \gdef-{\mathhyphen} \endgroup
\def\mathhyphen{\futurelet\foo\mhyphb}
\def\mhyphb{%
    \ifx\foo >\rightarrow \expandafter\@gobble
    \else \mathminus
    \fi}
\mathcode`\-="8000
\makeatother

Then

$x - y$ \qquad $x -> y$

Gives

enter image description here

share|improve this answer
add comment

One of my favorite kinds of trick is

\begingroup
<assignments>
\def\x{\endgroup<something>}\x

or variations thereof. For example (from the LaTeX kernel)

\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{
    \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
  \expandafter\endgroup\x
\def\strip@pt{\expandafter\rem@pt\the}

that could actually be simplified into

\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{\endgroup\def\rem@pt#1.#2PT}{#1\ifnum#2>\z@.#2\fi}
\def\strip@pt{\expandafter\rem@pt\the}

It takes a little bit of thinking to understand how this works. Such uses of \lowercase or \uppercase are abundant in Heiko Oberdiek's packages (which are full of other cunning tricks).

Another variation on the same theme, added after having seen Danie Els's answer. It's not necessary to define globally the active period by saying

\begingroup
  \lccode`\~=`\.
  \lowercase{\endgroup
    \def\unitcenterdot{\mathcode`\.=\string"8000 \def~{\,{\cdot}\,}}}
\protected\def\unit#1{%
    \begingroup\unitcenterdot
        \ensuremath{\mathrm{#1}}%
    \endgroup}
\protected\def\SI#1#2{\ensuremath{#1\,\unit{#2}}}

(the \string is to avoid problems with babel that may activate it). When \lowercase is executed, we have the definition

\def\unitcenterdot{\mathcode`\.=\string"8000 \def.{\,{\cdot}\,}}}

(but where the period is active). In this way we define the meaning of the active period only inside \unit. The commands are defined as robust: there is no problem in having them in arguments of other commands because there's no category code change, but at \write time TeX would find an active period that has no definition.

share|improve this answer
    
Alas, I am dense - what exactly do these do? –  Dean Serenevy Jun 3 '11 at 13:56
    
@Dean: look at Danie Els's answer. With \unit{N.m} you get the correct centered dot. Of course siunitx does the same thing and much more generally. –  egreg Jun 3 '11 at 14:00
add comment

Iterating through lists is fairly common and allows for compact code when using an appropriate definition. I often find that dealing with head/tail elements in the list requires special care. Here's an example (using etoolbox, although it holds for most processors/parsers):

enter image description here

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\newcommand{\printlist}[2][,]{%
  \renewcommand*{\do}[1]{#1##1}% How each item is processed
  \docsvlist{#2}}% Process CSV list
\begin{document}
$\printlist{1,2,3,4,5,6,7}$ \par
$\printlist[;]{a,b,c,d,e,f}$
\end{document}

Processing the list in this generic way - placing a delimiter/separator followed by the item - leaves the head with an unwanted delimiter. One way to get rid of this is to define a new delimiter that delays its use for one cycle:

enter image description here

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\newcommand{\printlist}[2][,]{%
  \def\itemdelim{\def\itemdelim{#1}}% Item delimiter delayed by one cycle
  \renewcommand*{\do}[1]{\itemdelim##1}% How each item is processed
  \docsvlist{#2}}% Process CSV list
\begin{document}
$\printlist{1,2,3,4,5,6,7}$ \par
$\printlist[;]{a,b,c,d,e,f}$
\end{document}

At the first cycle and call to \do, \itemdelim redefines itself so that it effectively sets nothing. At subsequent calls to \do, \itemdelim is fully-defined and just sets the delimiter.

Of course, this could be extended to whatever delay you want (not just a single cycle).

The alternative would be do condition on setting the delimiter based on the number of elements up to a certain point. Although intuitive, this is a little more cunning and uses TeX's macro expansion to its advantage.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.