• Non ci sono risultati.

Espressioni numeriche e dimensionali in ε-TEX

Nel documento Appunti di programmazione in L (pagine 146-150)

Un’importante estensione è quella che permette di valutare espressioni numeriche o relative a dimensioni senza adoperare le primitive aritmetiche di TEX: la differenza sostanziale è che la valutazione fatta daε-TEX è espandibile e non passa attraverso

assegnazioni. Il comando\numexprpuò essere usato in qualunque situazione in cui TEX si aspetta un 〈numero 〉.

Dopo\numexprsi può scrivere una qualsiasi espressione con numeri interi e operazioni aritmetiche, in cui le parentesi tonde possono essere usate con il significa-to matematico usuale. È ammessa anche la divisione, che però usa l’arrosignifica-tondamensignifica-to e non il troncamento come TEX con i registri\count. Tuttavia non è difficile definire l’operazione di divisione con troncamento:

\def\truncdiv#1#2{((#1-(#2-1)/2)/#2)}

Per esempio, il risultato della divisione fra 520 e 25 calcolato con l’aritmetica di TEX e con quella diε-TEX è diverso:

\count255=520 \divide\count255 by 25 \number\count255→ 20 \number\numexpr 520/25\relax→ 21

\number\numexpr\truncdiv{520}{25}\relax→ 20

Un’espressione numerica sta fra\numexpre\relax, ci si possono usare i simboli tradizionali (∗ per la moltiplicazione, però) e le parentesi, con le solite precedenze; la divisione produce sempre un risultato intero, arrotondato all’intero più vicino (il risultato di 3/2 è 2). Un’espressione numerica può essere usata in qualsiasi conte-sto dove TEX vorrebbe un 〈numero 〉 intero. Il token\relaxche si mette alla fine dell’espressione non farà parte dell’espansione.

La macro\truncdivusa una formula del calcolo numerico: se con /rsi indica la divisione intera con arrotondamento e con /tla divisione intera con troncamento, allora, purché i numeri siano positivi,

x/ty = (x − (y − 1)/r2)/ry.

Se i numeri non sono positivi, occorre una formula diversa.

Possiamo scrivere una macro la cui espansione sia l’ora attuale usando il registro interno\timeche all’inizio di una compilazione è caricato con il numero di minuti dalla mezzanotte.

\makeatletter \edef\etexnow{%

% Calcolo dell’ora

\number\numexpr\truncdiv{\time}{60}\relax:% % Calcolo dei minuti

\two@digits{%

\number\numexpr\time-60*\truncdiv{\time}{60}\relax}% }

\makeatother

Se usiamo questa macro otteniamo 18:41 che è l’ora in cui questo documento è stato compilato. Non dovrebbe essere difficile adattare questa macro alla rappresentazione di un numero in una base qualunque senza usare assegnazioni; la seguente, adattata da una di David Kastrup, produce la rappresentazione di un numero in base 16:

\def\basexvi#1{%

\ifcase\numexpr(#1)\relax

0\or 1\or 2\or 3\or 4\or 5\or 6\or 7\or 8\or 9\or A\or B\or C\or D\or E\or F% \else \expandafter\basexvi\expandafter {\number\numexpr((#1)-8)/16}% \expandafter\basexvi\expandafter {\number\numexpr(#1)-((#1)-8)/16*16}% \fi}

Se il numero dato come argomento è minore di 16 siamo coperti dai casi 0–15 (con gli \or); altrimenti viene passato a\basexviil quoziente della divisione per 16, facendo partire la ricorsione (si noti che viene usata essenzialmente la stessa formula usata per\truncdiv) e prodotto con\basexvistessa il resto della divisione. In questo modo le cifre sono prodotte nell’ordine corretto.

Il TEXbook mostra una macro che calcola il numero di token di cui è composto il suo argomento:

\def\length#1{{\count0=0 \getlength#1\end \number\count0}} \def\getlength#1{\ifx#1\end \let\next=\relax

\else\advance\count0 by1 \let\next=\getlength\fi \next} Fra i token dell’argomento non deve esserci\endche viene usato come ‘sentinella’ nella definizione di\getlengthné possono esserci graffe. Si tratta di una macro ricorsiva che mangia un token dell’argomento alla volta chiamando\getlengthfino a che il token che segue è\end.

Possiamo usare\numexprper avere lo stesso risultato, ma in modo che il valore possa essere usato in un contesto in cui ci serva la completa espandibilità, cioè nel testo di sostituzione di una\edefo tra\csnamee\endcsname:

\def\length#1{\number\numexpr 0\getlength#1\end} \def\getlength#1{% \ifx#1\end \expandafter\relax \else +1\expandafter\getlength \fi}

L’idea è esattamente la stessa, ma ora si aggiungono i token+12112all’espressione numerica che comincia con\numexpr 0fino a che il token che si trova è\end, quando invece si aggiunge\relaxper terminare l’espressione numerica.

Questo metodo ha una debolezza: si costringe TEX a valutare un’espressione 0+1+1+...+1potenzialmente molto lunga. Una macro di Paveł Jackowski risolve il problema in un altro modo:

\long\def\abacus#1{\@abacus#10} \long\def\@abacus#1#2#3{% \ifx#3#1% #2% \else \expandafter\@abacus

\expandafter#1\expandafter

{\number\numexpr#2+1\expandafter}% \fi}

La macro\abacusva chiamata in modo simile a\verb, cioè l’argomento (apparente) va delimitato con due token uguali. Così la chiamata

\abacus|abcdef| viene espansa in

\@addabacus|0abcdef|

Ora\@addabacusprende come argomenti i token|12,012ea11. Siccome il condizio-nale con cui comincia la sua espansione è falso, viene eseguito il ramo corrispondente che, dopo l’azione degli\expandafterè

\@abacus|{\numexpr0+1}bcdef|

ma, in realtà, l’espressione numerica è già stata calcolata ed è stata scritta così per mostrare che succede. Il comando\expandafterinterno alle graffe serve per toglie-re di mezzo il\fi; funziona perché quandoε-TEX valuta un’espressione numerica

espande completamente ciò che trova. Il condizionale continuerà a essere falso fino a che il terzo argomento di\@addabacusè|12. Vediamo un paio di esempi:

\abacus|Llanfairpwllgwyngyllgogerych% wyrndrobwllllantysiliogogogoch|→ 58 \abacus|Alte Br\"ucke|→ 11

\abacus\relax AlteBr\"ucke\relax→ 11

Si vede come gli spazi nella stringa da misurare sono ignorati, per rimediare si dovreb-be modificare il codice di categoria dello spazio, ma i token simbolici sono contati come uno.

Molto simile è il comando\dimexprche permette somme e sottrazioni fra di-mensioni e moltiplicazioni e divisioni per interi (sempre con arrotondamento) e con risultato espresso in punti tipografici. Si faccia attenzione che lo scalare intero va posto a destra del numero che deve moltiplicare. Esiste anche\glueexprcon cui si possono maneggiare lunghezze elastiche. All’inizio di questo documento abbiamo usato

\edef\@pnumwidth{\the\dimexpr\fontcharwd\font‘\1*3\relax} per ovviare al fatto che lo spazio riservato ai numeri di pagina nell’indice era trop-po stretto. LATEX conserva lo spazio riservato nella macro\@pnumwidthche non è un registro dimensionale. Il valore normale è 1.55 em, ma con i font Utopia-Fourier che sono impiegati in questo documento lo spazio è troppo piccolo per i numeri a tre cifre. Per i font Computer Modern si ha 1.55 em = 15.49623pt men-tre per i font Utopia-Fourier si ha 1.55 em = 13.64pt. I conti li ha fatti LATEX con 1.55em${}={}$\the\dimexpr1.55em\relax(quasi, si noti lo spazio sottile prima dell’unità di misura che mancherebbe con questo codice).

Si può usare\dimexprper ottenere l’arrotondamento alla seconda cifra decimale di numeri espressi con una precisione qualsiasi (si pensi a una tabella composta prendendo i dati da un file esterno). Il metodo è di Heiko Oberdiek:

\begingroup \catcode‘P=12 \catcode‘T=12 \lowercase{\endgroup \def\round@two#1.#2#3PT{#1.#2\@car#30\@nil}% } \newcommand{\roundtwo}[1]{% \expandafter\round@two\the\dimexpr #1pt+0.005pt\relax }

Il trucco con\lowercaseè già noto; gli argomenti di\round@twosono tre: il primo è delimitato dal punto decimale, il secondo non è delimitato, il terzo è delimitato dai tokenp12et12. Si usa il fatto che TEX esprime sempre una dimensione con la parte decimale, almeno con una cifra dopo il punto di separazione, perciò il secondo argomento di\round@twosarà sempre non vuoto; il terzo potrebbe esserlo e per questo si usa\@car#30\@nilin modo che se#3fosse vuoto, al suo posto ci sarebbe uno 0. Vediamo che la macro\roundtwofunziona:

\roundtwo{12.345}→ 12.35 \roundtwo{12.344}→ 12.34 \roundtwo{12.3}→ 12.30 \roundtwo{12}→ 12.00 \roundtwo{12,345}→ 12.35

Nell’ultimo esempio vediamo che il numero decimale può anche essere espresso con la virgola, anche se il risultato sarà comunque stampato con il punto. Per avere la virgola anche nel risultato occorre un altro trucco che mettiamo nella macro \roundtwocomma:

\def\roundtwocomma#1{\expandafter\r@two@c\number\roundtwo{#1}} \def\r@two@c#1.#2#3{#1,#2#3}

e un paio di esempi del funzionamento: \roundtwocomma{12.345}→ 12,35 \roundtwocomma{12,344}→ 12,34

Il comando\numberserve per arrivare all’espansione completa di\roundtwo. Possiamo adoperare\dimexprper modificare la definizione di\printdimin modo da non usare assegnazioni:

\def\printdim#1{\strip@pt\dimexpr#1\relax\unit{pt}}

(naturalmente si sta usando il comando\unitdel modulo italiano perbabel). Rispet-to alla definizione precedente questa è certamente più compatta; non c’è nemmeno il problema di dover moltiplicare per 1, dal momento che\dimexprignora le specifi-che per le dimensioni elastispecifi-che eventualmente passate come argomento. Vediamo un paio di esempi:

\printdim{\parindent}→ 0 pt

\texttt{\the\medskipamount}→6.0pt plus 2.0pt minus 2.0pt \printdim{\medskipamount}→ 6 pt

\printdim{1in}→ 72.26999 pt

Si lascia come esercizio di mettere insieme qualcosa del genere con la definizione pre-cedente di\roundtwoper ottenere solo due cifre significative in modo che l’ultimo comando dia 72.27 pt.

Nel documento Appunti di programmazione in L (pagine 146-150)