LaTeX-cursus Deel 5 in de praktijk: een eigen inleveropgaveklasse

\LaTeX kan veel beter overweg met formules dan Word of LibreOffice. Desalniettemin kan het veel werk zijn om lange uitwerkingen van inleveropgaven te \TeX-en. Het loont daarom om een eigen klasse te schrijven met daarin jouw meest gebruikte commando’s.

Om een eigen klasse te maken maak je een nieuw document met de extensie .cls. Bovenaan zet je welke versie van \LaTeX wordt gebruikt wordt. Dit doe je met \NeedsTeXFormat{LaTeX2e}, omdat je waarschijnlijk geen versie van voor 1994 wilt gebruiken. Om de computer te vertellen welke klasse je eigenlijk aan het maken bent, zet je daaronder ProvidesClass{opgave}. Deze twee commando’s zijn al genoeg voor een \LaTeX-klasse, maar we hebben er nog niet zoveel aan. Dit kunnen we verbeteren door de functionaliteit van een andere klasse te importeren. Sommige inleveropgaven zijn zo lang, dat het lijkt alsof ik een heel artikel moet schrijven, dus laten we de \article-klasse laden met \LoadClass[a4paper, 11pt]{article}. We zijn nu klaar om onze eigen inleveropgaveklasse te hekken.

In de mathmode maakt \LaTeX alle letters cursief en haalt alle spaties weg. Dit is meestal wat je wilt, maar dit betekent ook dat de ‘d’ in een differentiaal ($\text{d}x$) wat hulp nodig heeft om een mooie rechte ‘d’ te worden. Dit kan vervelend zijn met een tekst met veel integralen. Dit kan sneller met een eenvoudig nieuw commando: \newcommand{\dd}{\textbackslash,\text{d}}. Iedere keer als er \dd in een wiskundeomgeving staat, wordt er een korte spatie geplaatst: \, en en rechte ‘d’: \text{d}.

Met \newcommand kunnen ook parameters worden toegevoegd. Om snel een gemiddelde of een verwachtingswaarde in de tekst te zetten kun je het volgende definiëren:

\newcommand{\mean}[1]{\ensuremath{\left< #1 \right>}}

Hierin geeft [1] het aantal inputvelden van het nieuwe commando aan. Dat wil zeggen het aantal keer dat we iets achter het commando tussen accolades kunnen zetten. #1 is de parameter van het eerste inputveld. Dat is in dit geval makkelijk, want er is er maar één. Zo wordt \mean{x}: ⟨x⟩. Als je een commando definieert, weet je niet zeker of de gebruiker in mathmode is. Daarom is \ensuremath handig voor het maken van eigen commando’s, omdat \ensuremath een wiskundeomgeving start vanuit een tekstomgeving en in de wiskundeomgeving blijft als het wordt aangeroepen vanuit een wiskundeomgeving.

Een voorbeeld van wat ik vaak in \LaTeX schrijf, zijn afgeleiden. Het vervelende aan afgeleiden is dat er steeds breuken moeten worden geschreven en superscripten voor de hogere afgeleiden. Ik wil een commando \afg maken, waarmee ik:

  • De differentiatieoperator $\tfrac{\partial}{\partial t}$ krijg, als ik één parameter invul: \afg{t}
  • De afgeleide $\tfrac{\partial f}{\partial x}$ krijg als ik twee parameters invul: \afg{f}{x}
  • De ne afgeleide kan instellen met een optionele parameter tussen blokhaken: \afg{f}{x}[2]

Dit commando is makkelijker te maken met \NewDocumentCommand dan met \newcommand, omdat ik mijn commando wil ‘overloaden’. Dat wil zeggen dat ik andere dingen wil doen afhankelijk van het aantal opgegeven parameters. \NewDocumentCommand is onderdeel van de xparse-package. De xparse-package maakt het mogelijk om commando’s te maken met \LaTeX\ 3 in \LaTeX\ 2. Packages in een .cls-bestand laad je met \RequirePackage. Op deze manier kun je ook de preamble een stuk korter maken door je standaardpackages op deze manier in te laden, omdat je deze packages ook kunt gebruiken in jouw inleveropgave. \NewDocumentCommand ziet er als volgt uit:

\NewDocumentCommand{\mijnCommando}{opties voor parameters}{body}

De body werkt net zoals in \newcommand, maar voor de parameters zijn andere opties dan in \newcommand. Met de laatste kan men alleen tussen blokhaken het aantal parameters aangeven dat in het nieuwe commando gebruikt wordt, maar in \NewDocumentCommand is het een verplicht veld. Hierin wordt op volgorde van parameters #1 t/m #n met een letter aangegeven of het een optionele of verplichte parameter is[1]:

  • m van ‘mandatory’: geeft een verplicht veld aan, dus tussen accolades.
  • o van ‘optional’: geeft een optioneel veld aan, dus tussen blokhaken.
  • g van ‘overloaded’: geeft een optioneel veld aan, maar tussen accolades.

Het gebruik van g maakt het de gebruiker duidelijker dat de output van het commando anders kan reageren op het gebruiken van dit commando. Je ziet bij mijn eisenlijst dat de betekenis van het eerste inputveld verandert bij het toevoegen van een tweede parameter. Eerst staat de eerste parameter #1 onder de deelstreep en na het toevoegen van een tweede parameter staat #1 boven de deelstreep:
\afg{t} wordt:   \tfrac{\partial}{\partial t}
en   \afg{f}{x} wordt:   \tfrac{\partial f}{\partial x}
Het definiëren van afg ziet er als volgt uit:

\NewDocumentCommand{\afg}{mgo}{body}

Het aanroepen wordt dan:

\afg{#1 mandatory}{#2 overloaded}[#3 optional]

Ik mis nu slechts nog één detail: \LaTeX weet nog niet wat te doen met al onze mooie parameters. In de body kunnen we gebruik maken van IfValueTF. Wat dit doet is eenvoudig:

\IfValueTF{#n}{voer uit als #n bestaat}{voer uit als #n niet bestaat}

Dit kunnen we gebruiken voor zowel onze ‘overloaded’ als voor onze optionele parameter gebruiken. Voor de liefhebbers staat hieronder het commando voor de partiële afgeleide.

\NewDocumentCommand{\afg}{mgo}{
    \ensuremath{
        \IfValueTF{#3}{
            \IfValueTF{#2}{
                \frac{\partial^{#3} #1}{\partial #2^{#3}}
                }
                {
                \frac{\partial^{#3}}{\partial #1^{#3}}
                }
        }
        {
            \IfValueTF{#2}{
                \frac{\partial #1}{\partial #2}
            }
            {
                \frac{\partial}{\partial #1}
            }
        }
    }
}

Met de hierboven beschreven methoden kun je al ver komen met een eigen klasse, maar het zou leuk zijn, als je de nieuwe klasse ook met parameters kunt aanroepen. Dit kun je doen met DeclareOption:

\DeclareOption{parameter}{voer uit als 'parameter' is gegeven}

Je kunt op deze manier ook booleans uit of aan zetten, zodat je in het hele document deze parameter kunt gebruiken. Een voorbeeld waarmee je dit misschien wil doen, is de taal van het document. Als deze klasse wordt aangeroepen met \documentclass[english]{opgave} wil je de optie ‘english’ meegeven aan babel, maar ook, als je een kop- of voettekst hebt gedefinieerd, ‘Aantal pagina’s’ veranderen in ‘Number of pages’. Booleans kun je maken met het ietwat vreemde \newif[2]. Een voorbeeld van \newif is:

\newif\ifengels\engelsfalse

\DeclareOption{english}{\engelstrue}

Dit maakt een boolean \ifengels aan. Door daarna \engelsfalse te gebruiken wordt deze op ‘false’ gezet. \newif\ifengels maakt namelijk zelf \engelsfalse en \engelstrue aan. \DeclareOption{\engelstrue} zet \ifengels alleen op ‘true’, als deze klasse wordt aangeroepen met \documentclass}[english]{opgave}. Deze boolean kunnen we vervolgens gebruiken in een if-blok dat de taaloptie in babel stopt. Dit if-blok wordt afgesloten met \fi.

\ifengels\RequirePackage[english]{babel}
\else\RequirePackage[dutch]{babel}
\fi

Om te controleren of alle pagina’s compleet zijn, kan het handig zijn om op de eerste pagina te zetten hoeveel pagina’s er in totaal zijn. Dit kan met de packages fancyhdr en lastpage. Omdat we dit ook af en toe niet willen, is het handig dit alleen te doen, als er een optie ‘totpag’ in de klassenaanroep is meegegeven. Voor een tweede optie in documentclass gaat dit op dezelfde manier als in het vorige voorbeeld:

\newif\iftotpag\totpagfalse
\DeclareOption{totpag}{\totpagtrue}

Als iftotpag ‘true’ is, wordt de header weergegeven. Dit kan misschien het beste door de ‘fancypagestyle‘ van \maketitle aan te passen. Als \maketitle wordt aangeroepen, gebruikt de titelpagina de \fancypagestyle{plain}. Door deze aan te passen, kunnen we een header krijgen rechtsboven op de titelpagina.

\iftotpag
\fancypagestyle{plain}{
    \ifengels
    \fancyhead[R]{Number of pages: \pageref{LastPage}}
    \else
    \fancyhead[R]{Aantal pagina's: \pageref{LastPage}} 
    \fi
}
\fi

Tot slot wil ik nog code geven om de overgebleven opties van de klassenaanroep aan de article}-klasse te geven:

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions

Dit zet je voor \LoadClass[a4paper,11pt]{article}.

Veel plezier met \TeX-en en een gezegend 2016.

\end{document}

[1]  Er zijn veel meer opties dan deze drie. Zie de documentatie van xparse op CTAN.
[2]  Meer TeX-commando’s, die ook worden gebruikt in de A–Eskwadraat-packages kun je vinden op https://en.wikibooks.org/wiki/LaTeX/Plain\_TeX