Sei sulla pagina 1di 34

Generare documenti LATEX con diversi linguaggi di

programmazione
Roberto Giacomelli, Gianluca Pignalberi

Sommario
Questo articolo mostrerà come generare sorgenti Sorgente Documento
Utente/Editor TEX
LATEX per mezzo di programmi scritti in diversi TEX finale
linguaggi di programmazione. Vogliamo con ciò
presentare una soluzione generale per i progetti di
documentazione basati su processi automatici a
Figura 1: Flusso di lavoro manuale. L’utente crea e modifica
causa della complessità dei dati o per scelta appli- direttamente un documento TEX tramite un editor di testi.
cativa, così da ottenere rapporti magnificamente Tale documento verrà poi composto dal sistema TEX.
composti.

Abstract quelle fittizie; scrittura mediante l’uso una libreria


già predisposta, scrittura completa di una tabella
This paper will show how to generate LATEX source mediante scrittura su file di stringhe.
code with programs written in different program- Nel caso di scrittura parziale, lo farà a partire
ming languages. We aim at presenting a general da un modello — un template — riempiendo le
solution to those documentation projects based on parti bianche o sostituendo quelle fittizie; nel caso
automatic processes — because of data complexity di scrittura totale, scriverà tutto il documento, se
or on purpose — so to obtain beautifully typeset necessario inserendo il preambolo.
reports. Dopo aver chiarito il campo applicativo nel pa-
ragrafo 2, descriveremo il tipo di dato stringa e le
1 Introduzione relative operazioni nel paragrafo 3. Il paragrafo 4
introdurrà i modelli in termini generali.
Non diciamo niente di nuovo rimarcando il fatto Essendo i paragrafi fin qui elencati a carattere
che un documento LATEX è un documento di puro teorico, demanderemo ai successivi paragrafi 6, 7,
testo. La scelta di questa codifica, ridondante dal 8 e 10 di chiarire i dettagli di funzionamento dei
punto di vista dell’efficiente occupazione di spazio comandi specifici del linguaggio in esame e spiegare
su disco, offre due notevolissimi vantaggi: le tecniche usate con relativi vantaggi e svantaggi.
1. la possibilità di leggere e modificare ogni docu- Per ogni linguaggio presenteremo uno o più esem-
mento da qualunque editor di testo, interattivo pi in grado di fornire una panoramica sufficiente,
o meno; benché non esaustiva, delle operazioni necessarie a
rendere un programma TEX-friendly. Arriveremo
2. la possibilità di scrivere programmi e libre- a tale TEX-friendliness mediante programmazione
rie in grado di generare o modificare docu- bruta, dunque costruendo delle funzioni ad hoc, o
menti in maniera automatica e dipendente per mezzo di librerie preesistenti.
dall’elaborazione. I paragrafi 9 e 11 mostreranno due corposissi-
mi programmi in Lua e Python per risolvere dei
Del primo vantaggio non vale la pena discutere problemi reali quali la realizzazione della griglia
oltre, vista la sovrabbondanza di manuali e articoli di attività annuali di un gruppo di persone e un
che elencano questo punto in ogni lista di vantaggi report d’inventario di un esercizio commerciale con
di LATEX rispetto a ogni programma concorrente interrogazione di database.
che salvi in un formato binario.
Intendiamo invece soffermarci sul secondo van-
2 Il campo applicativo
taggio, più che mera estensione del primo. Lo fa-
remo fornendo degli esempi concreti, più o meno Il flusso di lavoro normale per generare un do-
semplici, tramite programmi scritti in diversi lin- cumento LATEX prevede che l’utente scriva ma-
guaggi di programmazione e scripting. Ognuno di nualmente i sorgenti usando un editor di testi
questi programmi scriverà in parte o completamen- (figura 1).
te un documento LATEX inserendogli i risultati di Nel caso della produzione automatica della docu-
un’elaborazione. Vedremo diversi metodi: scrittura mentazione invece, l’utente non interviene in alcun
a partire da un modello — un template — con modo sui sorgenti. Interagisce però con un’applica-
riempimento di parti bianche o con sostituzione di zione che elabora i dati e ne memorizza il risultato

40
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

la tupla e il dizionario) e presenta una sintassi più


Utente vicina al paradigma object oriented. Sono dispo-
nibili moltissime librerie, praticamente per tutte
le necessità e in particolare per la connessione a
database SQLite o PostgreSQL.
Strumenti simili integrati in un flusso di lavoro
Programma
Sorgente
TEX Documento possono essere le migliori soluzioni possibili in tutti
TEX finale quei casi che potremo definire artigianali dove
l’utente TEX vuole produrre documenti da insiemi
di dati dalle origini e dai formati più diversi.
Figura 2: Flusso di lavoro automatico. L’utente crea e Progetti in questo senso ne sono già stati rea-
utilizza un programma che genera il sorgente TEX. Tale lizzati moltissimi: si pensi all’applicazione Facile
documento verrà poi composto dal sistema TEX.
del comune di Napoli coadiuvata da Agostino De
Marco (De Marco e Cresci, 2011) o al recen-
in file perfettamente compilabili da uno dei motori te esempio di produzione di menù per ristorante
di composizione della famiglia TEX (figura 2). con un’applicazione web basata su php di Claudio
Stiamo entrando in un mondo completamente Fiandrino (Fiandrino, 2014). Ma anche la produ-
diverso dall’usuale: in questa prospettiva conside- zione di certificati di prova sui materiali secondo
reremo l’utente non come chi scrive il documento specifiche normative tecniche elaborato da Renato
ma come chi scrive il programma che scriverà il Battistin (Battistin, 2014).
documento. Un punto di vista che ci possiamo tro- Tutti questi progetti sono recenti perché le esi-
vare ad affrontare in tutti quei casi in cui i dati genze di reporting crescono sempre di più in un
del documento finale richiedano un’elaborazione mondo connesso e informatizzato come quello del-
automatica iniziale. le nostre società. D’altro canto grazie a Internet
Si pensi per esempio alla produzione di report sono disponibili strumenti software multipiattafor-
da database: è un campo applicativo dominato da ma, aperti e ben documentati dalle funzionalità
appositi strumenti informatici programmati in un davvero notevoli con cui gli utenti possono impe-
linguaggio ad alto livello da sviluppatori esperti che gnarsi per risolvere i problemi di elaborazione dati
operano in infrastrutture di rete. Nulla però vieta integrando TEX nel processo.
di esplorare soluzioni basate su TEX perché con
questo sistema il report è un testo comprensibile 3 L’elaborazione delle stringhe
direttamente modificabile dagli utenti.
Il nostro terreno di esplorazione sarà quindi La stringa è un tipo di dato di alto livello che
quello che percorrerebbero gli utenti del sistema consiste in una sequenza di caratteri per esprimere
TEX desiderosi di risolvere in proprio i problemi di il concetto di testo. Attraverso le stringhe il testo
reporting per raggiungere due principali obiettivi: può essere memorizzato in o caricato da un file
(dove per file intendiamo anche il flusso a video o
• report di ottima qualità tipografica; da tastiera e non solo un file su disco).
La locuzione “di alto livello” sta a indicare che
• report che si adattino facilmente a nuove
tale dato è, di fatto, qualcosa di più complesso:
esigenze per contenuto e grafica.
il C memorizza una stringa come un vettore di
Come vedremo più avanti, in confronto ai lin- caratteri gestito per mezzo del puntatore al pri-
guaggi di programmazione studiati per applicazioni mo elemento interpretando il carattere NULL (\0)
di sistema come il C/C++, i linguaggi di scripting come segnale di fine sequenza. Tale terminazione
non obbligano a preoccuparsi della gestione della è inserita quando si preme il tasto invio — co-
memoria e di molti altri dettagli, a utilizzare i pun- dificato come \cr\lf, \cr o \lf a seconda che il
tatori oppure a usare un compilatore per ottenere sistema operativo sia DOS/Windows, Mac pre-OS
gli eseguibili. X o Unix — e non fa parte della stringa.
In questo articolo mostreremo esempi concreti, La stringa è un tipo di dato di alto livello che
didattici o meno, in C e bash e in altri due linguaggi consiste in una sequenza di caratteri. La locuzione
di scripting che giudichiamo importanti per la loro “di alto livello” sta a indicare che tale dato è, di
potenziale utilità per gli utenti TEX: Lua e Python. fatto, qualcosa di più complesso: il C memorizza
In particolare, Lua (www.lua.org) ha una sintas- una stringa come un vettore di caratteri (o, meglio,
si essenziale e una sola struttura dati predefinita, un puntatore a carattere). Il tipo stringa espri-
la tabella, che può essere impiegata sia come lista me dunque esattamente il concetto di contenitore
che come dizionario e incorpora una libreria stan- di testo. Il testo può essere memorizzato in una
dard che offre all’utente solamente le funzionalità stringa che a sua volta può essere memorizzata,
indispensabili. cioè il suo contenuto può essere memorizzato, in
Il linguaggio Python (www.python.org), invece, un file di testo e viceversa. Naturalmente non c’è
si avvale di tre tipi fondamentali di dato (la lista, una restrizione sul tipo di caratteri contenuti in

41
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

una stringa, sebbene il C standard termini una Naturalmente, ai fini del nostro lavoro, sarebbe
stringa col carattere NULL (\0) e tale terminazione inutile non poter salvare delle stringhe più o meno
venga segnalata premendo invio, il cui carattere complesse in un file, così come lo sarebbe non
(\cr\lf, \cr o \lf a seconda che il sistema ope- poter memorizzare il contenuto di un file di testo
rativo sia DOS/Windows, Mac pre-OS X o Unix) in una o più stringhe; vedremo pertanto, sempre
pertanto non farà parte della stringa. nel paragrafo 5, alcune istruzioni per salvare dette
Per scrivere un programma che generi sorgenti stringhe in maniera permanente o acquisirle da un
sembra naturale usare le stringhe per tutte le ope- file.
razioni, ivi compresa la memorizzazione di quelle
parti del documento non soggette ad alcuna modi- 4 La tecnologia dei template
fica dipendente dall’elaborazione. L’uso che delle
stringhe possiamo fare è in buona misura deter- Come accennato nel paragrafo precedente, una
minato dalle caratteristiche di questi dati e dal- volta che sappiamo come trattare le stringhe è
le relative operazioni di manipolazione. Vediamo immediato pensare di scrivere un programma in
brevemente le possibili operazioni su una stringa, grado di memorizzare una documento il cui te-
tenendo conto che ogni linguaggio le implementerà sto sia contenuto in una o più stringhe e in cui
in base alle proprie caratteristiche e con una sin- eventualmente sostituire le parti variabili con il
tassi specifica, non sempre perfettamente coerente contenuto di stringhe impostate durante l’esecu-
dal punto di vista dell’utente: zione. Infatti tramite le operazioni sulle stringhe è
possibile costruire in modo efficiente brani testuali.
creazione: operazione con cui la stringa viene Man mano che si utilizzano queste tecniche con
creata, ed eventualmente inizializzata; di fat- successo diviene tuttavia chiaro che nel program-
to le viene riservata un’area di memoria ac- ma non esiste una separazione concettuale tra dati
cessibile tramite un’etichetta (il nome della e testo.
variabile di tipo stringa); Esaminiamo il caso di un classico problema di
mail merge, ovvero il problema di produrre una
lettura: operazione con cui il programma legge il
serie di lettere dal contenuto comune da personaliz-
contenuto della stringa;
zare a partire dai destinatari e dai relativi indirizzi.
scrittura: operazione con cui il programma scrive Possiamo certamente separare in file distinti l’elen-
del contenuto nell’area di memoria riservata co degli indirizzi, la logica del programma e il testo
alla stringa; della lettera ma, usando le tecniche di manipolazio-
ne delle stringhe, è proprio il concetto di modello
lunghezza: determinazione del numero dei carat- di lettera che rimarrà “nascosto” nel codice, così
teri della stringa; come la sua realizzazione fisica, cioè delle variabili
di tipo stringa contenenti il testo da trattare da
assegnazione: scrittura del contenuto di una
parte del programma. Ciò rende difficoltoso ap-
stringa in un’altra stringa;
portare qualunque modifica al modello limitando
concatenazione: operazione con cui il contenu- l’evoluzione dei contenuti e il riuso del codice.
to una stringa viene attaccato alla fine del 4.1 Cos’è un template
contenuto di un’altra stringa;
Il template, o modello, è un particolare tipo di docu-
comparazione: date due stringhe, se ne confron- mento testuale in cui una parte fissa e immutabile
ta il contenuto per determinare quale delle di contenuto si alterna a parti variabili chiamate
due venga alfabeticamente prima dell’altra; tag che contengono nomi individuati da sequenze
di caratteri detti delimitatori.
conversione: una stringa può essere convertita,
Il template replica nel campo informatico il con-
quando ce ne siano le condizioni, in numero,
cetto di modulo da compilare con l’ovvia differenza
in lista di token. . .
che gli spazi bianchi da riempire sono entità asso-
interpolazione: espansione di segnaposto presen- ciate al contenuto prodotto da un’elaborazione. A
ti in una stringa con dati corrispondenti per differenza di quanto detto sulle stringhe nel para-
la creazione di una nuova stringa; grafo precedente, il metodo di lavoro associato ai
template è differente perché:
ricerca: operazione mediante la quale si ricerca
la presenza, o l’assenza, di un carattere o di • il template introduce il modello del testo,
una stringa all’interno di un’altra stringa. un nuovo concetto per rappresentare il testo
finale;
Nel paragrafo 5, durante l’esposizione dei pro-
blemi da risolvere e delle relative soluzioni, ve- • il testo finale è la composizione delle parti
dremo come i linguaggi esaminati rendono dispo- letterali di testo del template e l’espansione dei
nibili al programmatore alcune delle operazioni tag con informazioni esterne — dette contesto
menzionate. — corrispondenti per struttura e nomi;

42
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

• un template può essere annidato in altri fase di costruzione che in quella di affinamento o
template per rappresentare un documento al mutare delle esigenze o delle specifiche.
strutturato; Occorre un’apposita libreria per utilizzare i tem-
plate con un dato linguaggio senza eccessivi sforzi
• il codice del programma è incentrato sulla programmativi. Sorprendentemente ci sono molte
logica e diviene più semplice; librerie poiché questa tecnologia è molto diffusa
sui server di rete per fornire all’utente pagine web
• la generazione di testo con il template può
dinamiche o file di puro testo scaricabili.
essere molto efficiente in funzione dell’imple-
Nelle prossime sezioni verranno illustrati esem-
mentazione che ne viene data in un linguaggio
pi d’uso dei template sia per Lua con la libreria
di programmazione.
lustache (Olivine-Labs, 2015), sia per Python
La corrispondenza tra template e contesto deve con la libreria Jinja (Jinja). Questi due templa-
essere biunivoca in termini sia di struttura che di te engine sono semplici da usare essendo scritti
nomi, costituendo l’interfaccia tra i dati e la forma per un linguaggio di scripting e sono adatti alla
che assumeranno nel documento finale. generazione di sorgenti TEX perché consentono di
cambiare i delimitatori di default a doppie paren-
4.2 I tag tesi graffe con altri che non interferiscono con il
All’interno del template i tag sono introdotti trami- sorgente finale.
te i caratteri prestabiliti dei delimitatori. Di solito
il delimitatore di apertura è una coppia di paren- 5 Applicazioni pratiche
tesi graffe aperte {{ mentre quello di chiusura è
I prossimi paragrafi raccolgono tutta la parte pra-
una coppia di parentesi graffe chiuse }}.
tica dell’articolo. Ogni paragrafo verterà su uno
Un esempio di template minimale è la seguente
specifico linguaggio, traducendo in pratica gli ele-
stringa dove un tag è associato a una variabile del
menti teorici visti nei due precedenti paragrafi e
contesto chiamata name:
mostrando degli esempi concreti in cui un pro-
"Hello {{ name }}!" gramma genera file di testo contenente codice che
uno dei motori di composizione del sistema TEX
I delimitatori — che possono essere reimpostati tradurrà in documenti finiti.
dall’utente — e gli spazi presenti all’interno dei Al termine degli esempi didattici in Lua e Py-
tag non verranno inseriti nel testo finale. Si trat- thon, due ulteriori paragrafi riporteranno due ap-
ta infatti di una sorta di ambiente codice. Come plicazioni reali. Ci proponiamo con esse di dare
vedremo, i tag possono essere di diverso tipo. indicazioni utili all’utente per definire il progetto
4.3 Utilizzo dei template nella sua fase iniziale e a svilupparlo successiva-
mente per adattarsi ai cambiamenti nelle sorgenti
In termini generali l’uso dei template comprende dati causati da nuove o inaspettate esigenze di
due fasi successive: gestione o documentazione.
La qualità del codice molto spesso dipenderà
compilazione un processo d’elaborazione del
anche dall’equilibrio tra le tre componenti prin-
template che fornirà un nuovo componente
cipali del processo di generazione: il programma,
binario;
il template e il sorgente TEX. Per esempio, non
resa (rendering) una o più esecuzioni del com- è sempre chiaro se far calcolare le somme econo-
ponente binario con dati d’ingresso struttura- miche dal programma o dal template, oppure se
ti e corrispondenti che forniranno altrettanti introdurre delle macro LATEX per svolgere com-
testi finali. piti che potrebbero essere affidati al template. È
buona norma basarsi sull’esperienza lasciandosi
In alcuni casi, la fase iniziale d’elaborazione del ispirare dal criterio di buona manutenibilità e svi-
template viene svolta durante la compilazione del luppo del progetto di documentazione e da quello
programma, anziché a ogni sua esecuzione, con dell’efficienza di esecuzione.
evidente incremento dell’efficienza: il programma Per la comprensione del codice d’esempio e delle
dovrà eseguire solamente la produzione dei docu- applicazioni presentate nell’articolo è necessario
menti finali. Nei casi tipici dei sistemi basati sui conoscere almeno i fondamenti dei linguaggi di pro-
linguaggi di scripting, invece, il template può es- grammazione che chiaramente non possono essere
sere compilato ogni volta che viene unito ai dati. forniti qui. Per Lua si consiglia la lettura del libro
È da tener presente inoltre che, per la produzione Ierusalimschy (2013) e del manuale di riferimen-
da programma di file sorgenti per il sistema TEX, to pubblicamente consultabile sul sito ufficiale. Per
non è tanto cruciale l’efficienza di elaborazione Python esistono molti libri e tutorial alcuni dei
quanto la semplicità e l’espressività della sintassi quali liberamente disponibili in rete. Per esempio,
dei tag del template: l’utente può modificare più Think Python (Downey, 2012), con particolare at-
agevolmente i documenti generati sia durante la tenzione riguardo alla versione 3 del linguaggio, tra

43
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

l’altro composto in LATEX. Oppure ancora si può 15 elif [[ $issue_number -lt 100 ]]; then
consultare il sito http://getpython3.com/ dove 16 add_on=$ylf"00"$issue_number;
è possibile documentarsi sulle basi del linguaggio 17 elif [[ $issue_number -lt 1000 ]]; then
— per esempio con il libro scaricabile Dive into Py- 18 add_on=$ylf"0"$issue_number;
thon 3 (Pilgrim, 2009) — e su temi specifici come 19 else add_on=$ylf$issue_number;
fi
la gestione Unicode delle stringhe. Il riferimento
20

21
per il C è senz’altro il Kernighan & Ritchie (Kerni- 22 #Analizza la modalità di generazione,
ghan e Ritchie, 2004) ma vale la pena consultare 23 #crea i file appropriati e genera
anche il Klemens (2014) per tenersi aggiornati 24 #l'issn corretto
sulle modernizzazioni del linguaggio, specialmente 25 lv=`cat latest_version`
sugli ampliamenti ai tipi di dato e all’elaborazione 26 if [ "$1" = "print" ]; then
delle stringhe. Infine, per bash è possibile consul- 27 issn="977182823500 $add_on"
tare i siti ABS guide, GNU Bash e il manuale in 28 echo "Genero issn per $issn..."
linea (man bash da un qualunque terminale *nix). 29 cat seconda_head.tex issn_print.tex \
30 seconda_tail.tex > seconda.tex;
barcode -E -e ean -u cm -g 4x1.5 -b \
6 Bash 31

32 "$issn" -o issn_code.eps
Bash è un interprete di linguaggio sh-compatibile1 33 if [ "$lv" = "online" ]; then
che esegue comandi letti dallo standard input o da 34 sed -i arstexnica.tex -e \
un file. Bash incorpora anche delle caratteristiche 35 's/%colorlinks/colorlinks/g; \
s/%citecolor/citecolor/g; \
utili mutuate da Korn e C shell (ksh e csh). 36

37 s/%linkcolor/linkcolor/g; \
Descriveremo alcune delle operazioni realizzate
38 s/%urlcolor/urlcolor/g'
da uno script bash durante la compilazione di un 39 fi
numero di ArsTEXnica. Avremo modo di osservare 40 elif [ "$1" = "online" ]; then
che tutte le operazioni si basano sulla creazione di 41 issn="977182823600 $add_on"
una stringa di testo che poi verrà inserita in una 42 echo "Genero issn per $issn..."
posizione specifica di un file .tex predisposto. La 43 cat seconda_head.tex issn_online.tex \
manipolazione delle stringhe2 in bash è senz’altro 44 seconda_tail.tex > seconda.tex;
più agevole della manipolazione dei numeri, però 45 barcode -E -e ean -u cm -g 4x1.5 -b \
avremo spesso bisogno di ricorrere a programmi di 46 "$issn" -o issn_code.eps
sistema3 per isolare quelle parti di una stringa di 47 if [ "$lv" = "print" ]; then
sed -i arstexnica.tex -e \
nostro interesse. 48

49 's/colorlinks/%colorlinks/g; \
Il sorgente che segue, una porzione minima dello
s/citecolor/%citecolor/g; \
script di compilazione di ArsTEXnica, determina 51
50

s/linkcolor/%linkcolor/g; \
l’anno della rivista, genera l’issn adeguato alla ver- 52 s/urlcolor/%urlcolor/g'
sione (a stampa e online) e, oltre alla generazione 53 fi
del codice a barre, inserisce i dati nella seconda di 54 fi
copertina. 55 sed -i arstexnica.tex -e "s/XX, YY/ \
56 $issue_number, $current_year/g"
1 #Determina l'anno corrente; serve per
2 #l'add-on dell'issn e, poi, per i dati
Vediamo in dettaglio cosa fa questo codice ricor-
3 #di uscita di ArsTeXnica
4 current_year=`date +%Y` dando che le righe inizianti con # sono dei com-
5 ylf=`echo $current_year | cut -b 4` menti. La riga 4 fa sì che venga eseguito per primo
6 il comando (esterno) tra apici inversi e questo
7 #Estrae i dati dell'uscita corrente estrae l’anno (di quattro cifre) dalla data corrente.
8 #e determina l'add-on La stringa così ottenuta (non è un numero) vie-
9 current_issue=`pwd | awk -F / '{print $NF}'` ne assegnata a una variabile. La riga 5 assegna a
10 issue_name="arstexnica_"$current_issue una seconda variabile l’ultima cifra dell’anno. Per
11 issue_number=`echo $current_issue | \ ottenere questo risultato in bash occorre inviare
12 cut -d_ -f2` l’output del comando esterno echo (che stampa
if [[ $issue_number -lt 10 ]]; then
13
a video una riga di testo) sulla variabile appena
14 add_on=$ylf"000"$issue_number;
assegnata, cioè proprio la stringa-anno, al coman-
1. Mentre sh sta per Bourne shell, bash sta per Bourne- do esterno cut, che rimuove delle sezioni da ogni
again shell. riga di un file stampando le rimanenti; in questo
2. Di fatto, le uniche operazioni immediate di manipo-
lazione di stringhe in bash sono la concatenazione e il
caso, stampa il solo quarto byte dopo aver tolto
confronto. gli altri. La semantica della riga di codice appena
3. La locuzione programmi di sistema indica quei fil- vista è: esegui per primo il comando entro gli apici
tri — semplici programmi a riga di comando che com- retroversi e assegna il suo output alla variabile.
piono un’operazione piuttosto specifica — normalmente
presenti nei sistemi Unix sotto le directory /bin, /usr/bin, La riga 9 assegna a una variabile il nome
/usr/local/bin. della directory corrente, quella contenente l’u-

44
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

scita. Il nome convenzionale di tale directory è bash alle due stringhe di comodo XX e YY contenute
Numero_<numero>. Dunque la variabile, ipotizzan- nel master .tex (righe 55–56), in questo frangente
do di parlare del numero 18, conterrà la stringa strutturato come un modello i cui campi variabili
Numero_18. La riga 10 crea una variabile (sem- non sono delimitati da tag ma costituiti da testo
pre stringa) a cui assegna il nome dell’uscita di comodo.
così composto: arstexnica_<contenuto della C’è da chiedersi con quale difficoltà e in quanto
variabile assegnata alla riga 9>. Nelle ri- tempo avremmo potuto realizzare questo pezzet-
ghe 11–12 estraiamo il numero dell’uscita e lo to di script se avessimo dovuto elaborare dei file
assegniamo a una variabile (c’è da ripetere “di binari?
tipo stringa”?) col solito meccanismo echo + cut.
Ormai avremo notato che l’assegnazione di una 7 C
variabile in bash implica la scrittura del solo no-
me della variabile; per recuperare il valore in essa In questo paragrafo vedremo come istruire un pro-
contenuta dobbiamo anteporre il simbolo dollaro gramma C a scrivere parte di un documento LATEX.
($) al nome della variabile. Per questo esempio didattico scegliamo di calcolare
i primi n numeri di Fibonacci e di scriverli in una
Ora abbiamo tutti i dati per formare quel dato
tabella immagazzinata in un file. Per dare un po’
variabile del codice issn del giornale noto come
più di informazioni ai lettori, forniremo un altro
add-on: ultima cifra dell’anno e numero. Questi
esempio, sempre didattico, in cui generiamo casual-
due numeri dovranno essere separati dal numero
mente un diagramma scatter (a dispersione) di cui
di zeri necessario per formare un numero di cin-
scriveremo il grafico in un template TikZ (Tantau,
que cifre. Dunque le righe 13–20 si incaricano di
2015).
confrontare (mediante conversione stringa-numero
implicita nelle doppie parentesi quadre dell’if) il 7.1 C e tabelle LATEX
numero di uscita con 10, 100 e 1000. Per un nu- Il primo esempio mostrato calcola i primi n nu-
mero, essere minore di una di quelle cifre, implica meri di Fibonacci, che ricordiamo essere dati dal-
essere composto da una, due o tre cifre. Sfruttiamo la seguente formula (ricorsiva perché calcolata in
questo dato per iniettare nella variabile add-on il termini di sé stessa):
numero opportuno di zeri.

Siamo pronti a iniziare la generazione del nume- n se n 6 1
f (n) =
ro. Possiamo compilare la rivista in due modalità: f (n − 1) + f (n − 2) altrimenti
stampa (print) o elettronica (online). Inseriamo
in una variabile la modalità dell’ultima compilazio- La più banale funzione C che calcoli questo
ne effettuata (salvata in un file di testo, riga 25). algoritmo è la seguente:
Stabiliamo la modalità di compilazione corrente
1 long
analizzando il parametro fornito al comando (righe 2 fibonacci (long numero)
26 e 40) e stabiliamo di conseguenza l’issn corretto 3 {
(righe 27 e 41; l’add-on è lo stesso per entrambe 4 if (numero < 0)
le versioni). Quindi generiamo il file della seconda 5 {
di copertina, dato dalla concatenazione di tre file 6 printf ("Numero errato\n");
di testo tramite il comando esterno cat (righe 29– 7 exit (0);
30 e 43–44), e il codice a barre (comando esterno 8 }
barcode, righe 31–32 e 45–46). 9 if (numero <= 1)
10 return numero;
A seconda che la compilazione corrente sia per 11 else
la versione a stampa o elettronica e l’ultima ese- 12 return (fibonacci (numero - 1) +
guita sia stata l’opposta (righe 33 e 47), bisogna 13 fibonacci (numero - 2));
commentare o decommentare le impostazioni re- 14
lative ai link colorati nel file arstexnica.tex: le 15 }
vogliamo nella versione elettronica ma non in quel-
la a stampa (righe 34–38 e 48–52). Naturalmente, Vediamo la perfetta corrispondenza dei return,
se la compilazione corrente ha la stessa modalità cioè della funzione che restituisce il valore d’uscita
della precedente, non c’è bisogno di fare alcunché di una funzione (o del programma se la funzione è
dato che le impostazioni sono già quelle necessarie. main()) con le definizioni della formula.4 Abbiamo
Anche quest’operazione è affidata a un coman- scritto una funzione C ricorsiva. Dal momento che
do esterno: sed, o Stream EDitor, un editor non le funzioni ricorsive sono molto onerose dal punto
interattivo. 4. Stiamo volutamente tralasciando una banale questione
L’ultima operazione è inserire i dati relativi di efficienza: scambiando le due condizioni dell’if rendiamo
il programma leggermente più efficiente perché saranno
al numero e all’anno della rivista nel master più numerosi i casi in cui calcoliamo la funzione di numeri
arstexnica.tex. Di nuovo grazie a sed sostituia- superiori a 1, dunque non saltiamo una parte di codice per
mo i dati contenuti nelle due variabili costruite con andare al ramo else.

45
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

di vista dei tempi di calcolo e dell’impiego di cpu, 12 {


decidiamo di usare una versione iterativa della 13 long i, numero;
funzione appena vista: 14 char ft[50];
15 FILE *filetab;
1 long 16

2 fibonacci (long numero) 17 numero = atoi (*++argv);


3 { 18 strcpy (ft, *++argv);
4 if (numero < 0) 19 strcat (ft, "_tabella.tex");
5 { 20 if ((filetab =
6 printf ("Numero errato\n"); 21 open_file (ft, "w")) == NULL)
7 exit (0); 22 exit (1);
8 } 23 fprintf (filetab,
9 if (numero <= 1) 24 "\\begin{tabular}{rr}\n\\hline\n");
10 return numero; 25 fprintf (filetab,
11 else 26 "\\textsc{i} & \\textsc{i-esimo \
12 { 27 numero di Fibonacci}\\\\\n");
13 int menouno = 1, menodue = 0, i; 28 fprintf (filetab, "\\hline\n");
14 long res = 0; 29 printf
15 for (i = 2; i <= numero; i++) 30 ("Calcola i primi %d numeri di \
16 { 31 Fibonacci\n", numero);
17 res = (menouno + menodue); 32 for (i = 0; i < numero; i++)
18 menodue = menouno; 33 fprintf (filetab, "%ld & %ld\\\\\n",
19 menouno = res; 34 i + 1, fibonacci (i));
20 } 35 /*printf("Scrittura del file %s\n", ft); */
21 return res; 36 fprintf (filetab, "\\hline\n");
22 } 37 fprintf (filetab, "\\end{tabular}\n");
23 } 38 fclose (filetab);
39 return 0;
Non stiamo qui a discutere la correttezza5 o 40 }
l’efficienza6 di questa versione della funzione, non 41
essendo questo il luogo deputato. La useremo però 42 long
per costruirci intorno un programma7 che prenda 43 fibonacci (long numero)
dalla riga di comando il numero di numeri di Fibo- 44 {
nacci da calcolare e il nome del file in cui salvare 45 if (numero < 0)
il codice LATEX e termini senza output ma abbia 46 {
printf ("Numero errato\n");
creato una tabella perfettamente inseribile in un
47

48 exit (0);
documento LATEX e contenente i dati calcolati. 49 }
Vediamo il programma principale (di fatto, la 50 if (numero <= 1)
funzione main() e poco altro): 51 return numero;
52 else
1 #include <stdio.h>
53 {
2 #include <stdlib.h>
54 int menouno = 1, menodue = 0, i;
3 #include <string.h>
55 long res = 0;
4
56
5 long fibonacci (long);
57 for (i = 2; i <= numero; i++)
6 FILE *open_file (char *, char *);
58 {
7
59 res = (menouno + menodue);
8 int
60 menodue = menouno;
9 main (argc, argv)
61 menouno = res;
10 int argc;
62 }
11 char *argv[];
63 return res;
5. Pur non spiegando i dettagli della funzione iterativa, i 64 }
risultati delle due versioni sono identici. 65 }
6. La velocità di calcolo sembra un argomento capzioso 66
in un banale esempio didattico, eppure la versione iterativa 67 FILE *
del programma finale, scrittura del file compresa, termina 68 open_file (char *nomefile,
l’esecuzione del calcolo dei primi 45 numeri di Fibonacci
in un millisecondo sulla macchina di riferimento, quella
69 char *accesso)
ricorsiva in circa 21 secondi (circa 21 000 volte più lenta). 70 {
7. Il programma, didattico, non tiene conto di molti 71 FILE *fp;
aspetti cruciali in un programma di produzione quali, per 72
esempio, il controllo preventivo sull’input, il controllo degli 73 if ((fp =
overflow, l’interfaccia utente e il controllo sull’esistenza e 74 fopen (nomefile,
l’eventuale volontà di sovrascrivere i file. Quello che conta,
75 accesso)) == NULL)
ai fini dell’articolo, è mostrare come le funzioni di libreria
del C permettono di gestire le stringhe e di scriverle su file. 76 fprintf (stderr,

46
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

77 "Errore nell'apertura di %s\n", sintassi dell’istruzione è più complessa: dice, lette-


78 nomefile); ralmente, “poiché argv punta all’inizio del primo
79 return fp; elemento sulla riga (cioè il nome del programma
80 } in esecuzione), spostati al secondo elemento (il ++
è un incremento di 1; se postfisso, viene eseguito
e discutiamone il funzionamento, con particolare dopo che la funzione ha preso in input, e processa-
riferimento alle istruzioni di manipolazione delle to, il parametro; se prefisso, prima viene eseguito
stringhe e gestione dei file. l’incremento e poi passato il parametro alla funzio-
Le prime tre righe includono delle librerie ne- ne e processato. Nel caso in esame, incrementare
cessarie perché forniscono dei comandi inesistenti di 1 significa saltare al parametro successivo) e
nel C.8 La quinta e la sesta riga dichiarano due convertilo a numero”.
funzioni: fibonacci(), che prende un input di ti-
La riga 18 copia il terzo elemento della riga
po long int (intero di quattro byte con segno)
di comando (secondo parametro, nome del file
e restituisce un risultato di tipo long int (int è
LATEX contenente la tabella finale) nella variabile
implicito e, pertanto, omesso) e open_file(), che
ft (strcpy sta per string copy). Quindi la riga
prende in input due variabili di tipo puntatore a
19 completa il contenuto della variabile ft, cioè
carattere (stringhe) e restituisce un puntatore a
concatena al suo contenuto (strcat sta per string
file. La definizione9 delle funzioni avverrà dopo la
concatenate) la stringa _tabella.ft (per una di-
funzione main(), cioè dopo il programma principa-
scussione più approfondita sulla tecnica della con-
le. Alle righe 8–9 troviamo proprio la dichiarazione
catenazione delle stringhe, si veda più avanti il
della funzione main() che prende in input degli
paragrafo 8.4). Naturalmente, se il nome finale
argomenti (e argc e argv sono i nomi standard,
eccede i 49 caratteri (il cinquantesimo serve a con-
ma non obbligatori, delle variabili contenenti il nu-
tenere il \0 di fine stringa), verrà tagliato. Le righe
mero di argomenti ricevuto sulla riga di comando
19–21, aprendo in scrittura il file che conterrà la ta-
e le stringhe contenenti gli argomenti effettivi) e
bella LATEX, dicono letteralmente: “se il puntatore
restituisce in output un valore intero con segno.10
a file assegnato alla variabile filetab dalla funzio-
Le righe 10 e 11 dichiarano le variabili di input di
ne open_file() (la discuteremo alla fine) è NULL
main(), quindi con la successiva parentesi graffa
(cioè l’apertura del file in scrittura non è andata a
(riga 12) inizia il corpo del programma. Nelle ri-
buon fine) termina l’esecuzione (riga 22) e esci dan-
ghe 13–15 vengono dichiarate alcune variabili: due
do in output il valore 1”. Ottenere un valore diverso
long int, una stringa (il C considera gli array di
da NULL significa che l’apertura è andata a buon
caratteri come stringhe. Notate che la lunghezza
fine e possiamo andare avanti con l’esecuzione.
massima è predeterminata — il C lo prevede espli-
citamente, a meno di allocare dinamicamente lo Le righe 23–28 (una è interrotta con un \ per
spazio durante l’esecuzione — e, cattiva pratica, motivi di impaginazione; è il modo in cui il C ci
è scritta numericamente nella variabile. Il prossi- permette di mandare a capo delle sequenze di ca-
mo esempio mostrerà una pratica migliore) e un ratteri tra doppi apici, cioè delle stringhe, senza
puntatore a file. dare errore) scrivono nel file le prime righe del
Come abbiamo detto, i parametri d’ingresso sono codice LATEX della tabella. Essendo bufferizzata,
visti come delle stringhe; poiché il primo parametro la scrittura potrebbe non avvenire nel momento
è un numero, bisogna effettuare una conversione. esatto in cui l’istruzione viene eseguita. Le righe
Dunque la riga 17 esegue la conversione: atoi() 29–31 sono solo un memento per l’utente: stam-
(array [of char] to integer) prende in input una pano a video che il programma calcola i primi n
stringa e restituisce un numero, corrispondente al numeri di Fibonacci. Tale stampa è sempre buf-
numero rappresentato nella stringa. In realtà la ferizzata. Le righe 32–34 calcolano effettivamente
i numeri voluti e contemporaneamente li scrivo-
8. Il C, linguaggio molto efficiente perché creato per scri- no nella tabella (nella riga 33 vediamo l’uso della
vere sistemi operativi, è anche molto avaro di comandi. tecnica dei campi variabili, discussa in maggior
Moltissimi comandi usati quotidianamente sono forniti dal-
la libreria standard, parte integrante del linguaggio. Molti
dettaglio più avanti, al paragrafo 8.3). Ovviamen-
programmatori in cobol e altri linguaggi “storici” pole- te fibonacci(0) rappresenta il primo numero di
mizzano proprio su questa “avarizia” di comandi, quando i Fibonacci, ecco il perché dell’i+1 nella fprintf().
“loro” standard forniscono decine di comandi e funzioni per La riga 35, sempre un memento per l’utente, è
molte operazioni fondamentali quali l’I/O.
9. La differenza tra dichiarazione e definizione è, in so-
commentata e pertanto non eseguita.
stanza, che la prima serve a indicare una forma (nome della Le righe 36 e 37 scrivono nel file tabella i comandi
variabile o della funzione e tipi di ingresso e uscita) mentre di chiusura della stessa; la riga 38 chiude il file
la seconda un contenuto (valore della variabile o serie di
istruzioni della funzione e nomi delle variabili di input e e la riga 39 termina l’esecuzione del programma
output). restituendo il valore 0 che, per convenzione, in C
10. A differenza di long int, che è definito come un indica il buon fine.
intero con segno di 4 byte, int è definito come un in-
tero con segno di 2 o 4 byte. La dimensione dipende Vediamo ora la funzione open_file(). Poiché
dall’implementazione. la funzione deputata del C (fopen()) non segnala

47
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

Tabella 1: Risultato del calcolo dei primi n numeri di


Fibonacci. 1 \begin{tikzpicture}
2 \draw[very thin,color=gray] (0,0.01) grid (10,10);
i i-esimo numero di Fibonacci 3 \draw[->] (-0.2,0) -- (10.5,0) node[right] {$x$};
1 0 4 \draw[->] (0,-0.2) -- (0,10.5) node[above] {$y$};
2 1 5 %---riga da riempire
3 1 6 \draw[color=black!30!green] () node {$\times$};
4 2 7 \end{tikzpicture}
5 3
6 5 È facile vedere che la seconda riga disegna la gri-
7 8 glia del piano cartesiano e le righe 3 e 4 disegnano
8 13 gli assi del piano. La riga 5 contiene il commento
9 21 che controlleremo per capire qual è il punto in cui
10 34 inserire i dati.12 La riga successiva al commento
11 55 è quella da riempire coi dati generati e ripetere
12 89 tante volte quante sono le coordinate generate.
13 144
14 233 1 #include <stdio.h>
#include <stdlib.h>
15 377 2

3 #include <string.h>
16 610
4 #include <time.h>
17 987 5
18 1597 6 #define MAXRIGA 2048
19 2584 7 #define CONTROLLO "%---riga da riempire"
20 4181 8

9 FILE *open_file (char *, char *);


10

11 int
niente all’utente ma solo al programma tramite 12 main (argc, argv)
il parametro di ritorno, la nuova funzione colma 13 int argc;
questa lacuna stampando a video l’eventuale errore 14 char *argv[];
in apertura. Notiamo che, anziché una printf(), 15 {
abbiamo usato una fprintf() (stampa su file) 16 long i, numero, cr;
che stampi su stderr. Quest’ultimo è uno dei “fi- 17 int x, y;
le” standard (gli altri due sono stdin e stdout); 18 char nomefile[MAXRIGA], fg[MAXRIGA],
tmpl[] =
ridirige sul terminale quello che riceve e lo fa in
19

20 "template.tex", riga[MAXRIGA];
maniera non bufferizzata, dunque la scrittura a vi- 21 FILE *filegraf, *template;
deo avviene nel momento esatto in cui l’istruzione 22
viene eseguita. Quindi esce restituendo il puntatore 23 numero = atoi (*++argv);
a file (o NULL in caso di errore). 24 strcpy (fg, *++argv);
Eseguiamo il programma, dopo averlo com- 25 strcat (fg, "_grafico.tex");
if ((filegraf =
pilato (per compilarlo in Linux basta gcc -o 26
open_file (fg, "w")) == NULL)
nfibonacci nfibonacci.c), per fargli calcolare i
27

28 exit (1);
primi 20 numeri di Fibonacci. Il risultato è visibile 29 if ((template =
nella tabella 1. 30 open_file (tmpl, "r")) == NULL)
31 exit (1);
7.2 C e template di grafici 32 srand (time (NULL));
33 while (fgets
Vediamo ora un secondo esempio in cui generiamo 34 (riga, MAXRIGA - 1,
template) != NULL)
casualmente una serie di coordinate cartesiane in- 35
36 if (strncmp (riga, CONTROLLO,
tere comprese tra (0,0) e (10,10) e le riportiamo su
37 strlen(CONTROLLO)))
un grafico TikZ la cui “ossatura” è contenuta in un 38 fputs (riga, filegraf);
modello chiamato template.tex. Poiché l’esempio 39 else
è piuttosto limitato, decidiamo di abbandonare i 40 {
tag in favore di una riga di commento e di uno 41 fgets (riga, MAXRIGA - 1,
spazio da riempire in maniera standard. Di seguito 42 template);
il contenuto del template.11 43 printf
12. Vista la dimensione e la semplicità del template,
sarebbe stato più immediato andare direttamente al punto
11. Lasciamo volutamente debordare il codice per far da modificare tramite una fseek(); non è però questo quello
capire al lettore che ogni riga letta dal programma C è che vogliamo mostrare, ma un metodo di lavoro più generale
esattamente quella qui stampata. che discuteremo più avanti.

48
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

44 ("Genera %d coppie di coordinate \ il grafico in sola scrittura e quello contenente il


45 di un diagramma a dispersione\n", template in sola lettura.
46 numero); La riga 32 inizializza il generatore di numeri
47 for (i = 0; i < numero; i++) casuali prendendo come seme il valore restituito
48 { dalla funzione time(). Altri linguaggi forniscono
for (cr = 0;
una funzione equivalente senza parametri di input
49

50 riga[cr] != '('; cr++)


51 fputc (riga[cr],
chiamata randomize.
52 filegraf); Le righe 33–63 sono quelle che fanno tutto. Cer-
53 x = (int) rand () % 11; chiamo di raccontarle in maniera discorsiva, co-
54 y = (int) rand () % 11; me se stessimo raccontando una storia. Prima di
55 fprintf (filegraf, "(%d,%d", raccontare, sarà buona cosa descrivere in termini
56 x, y); sommari alcune istruzioni introdotte qui per la
57 for (cr++; riga[cr] != '\n'; prima volta:
58 cr++)
59 fputc (riga[cr], while è uno dei cicli del C; esegue il corpo del
60 filegraf); while (cioè tutto quanto contenuto nelle pa-
61 fputc ('\n', filegraf); rentesi graffe successive o, in mancanza di
62 } parentesi graffe, solo l’istruzione seguente) fin-
63 } tanto che la condizione del ciclo è vera. Al
64 printf ("Scrittura del file %s\n", contrario, esce;
65 fg);
66 fclose (template); fgets() legge una riga da un file (di testo) termi-
67 fclose (filegraf); nata da \n o dalla dimensione fornita come
68 return 0; secondo parametro;
69 }
70 strncmp() compara i primi n caratteri delle due
71 FILE * stringhe fornite come argomento;
72 open_file (char *nomefile,
73 char *accesso) strlen() restituisce la lunghezza della stringa
74 { fornita come argomento;
75 FILE *fp;
76 fputs() scrive una stringa in un file di testo;
77 if ((fp =
78 fopen (nomefile, for è un altro ciclo del C; la sua sintassi è piutto-
79 accesso)) == NULL) sto articolata: all’interno delle parentesi tonde
80 fprintf (stderr, possono essere fornite tre espressioni, ognuna
81 "Errore nell'apertura di %s\n", delle quali obbligatoriamente separata dal ‘;’.
82 nomefile); La prima espressione funge da inizializzazione
83 return fp; del ciclo, cioè viene eseguita solo all’entrata
84 } nel ciclo; la seconda espressione viene eseguita
all’inizio di ogni iterazione successiva; infine,
Passiamo alla descrizione di questo esempio sal- la terza viene eseguita alla fine di ogni iterazio-
tando quelle parti di codice già discusse all’esempio ne. L’espressione for (;;) è perfettamente
precedente. lecita in C e viene usata per eseguire cicli
Le righe 6 e 7 contengono la direttiva infiniti;
#define, cioè un’istruzione che dice al compila-
tore: “ogni volta che nel sorgente trovi i termi- rand() restituisce un valore intero, compreso tra 0
ni MAXRIGA e CONTROLLO, sostituiscili con 2048 e e RAND_MAX, (pseudo)casuale estratto da una
"%---riga da riempire". A differenza dell’esem- lista di numeri eventualmente generata da
pio precedente, in cui il numero 50 era contenuto srand();
nel sorgente e la cui sostituzione comportava di ri-
% calcola la “divisione modulo”, cioè fornisce il
cercarlo in tutto il codice (e non è detto che tutte le
resto di una divisione tra operandi interi.
occorrenze di 50 abbiano lo stesso significato, cioè
può darsi che in alcune parti il 50 debba rimanere Ora possiamo raccontare la parte di codice ri-
tale e non essere sostituito), in questo caso, invece, manente: per ogni riga letta dal template (33–35)
basta modificarlo in una sola riga contenuta all’ini- verifichiamo che non sia la riga “di controllo” (36–
zio del sorgente. Le righe 15–19 contengono delle 37) per riscriverla sul file del grafico (38). Nel caso
definizioni di variabili locali (cioè visibili solo alla la riga sia quella “di controllo” (39), leggiamo la
funzione in cui sono definite) utili al programma. riga successiva (41–42) e scriviamola nel file del
Le righe 23–31 sono equivalenti a quanto già grafico (49–52 e 55–61) tante volte quante sono le
visto nell’esempio di Fibonacci. L’unica differenza coppie di coordinate da generare (47), “iniettan-
è che ora apriamo due file: quello che conterrà do” ogni riga con la coppia di coordinate intere

49
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

× × × × × × × ×

× × × ×

× × × × ×

× × × × × × × × ×

× × × ×

× × × × × × × × ×

× × × × ×

× × × ×

× × × × × ×

× × × × × × × ×

× × × × × × × × x

Figura 3: Grafico del diagramma scatter relativo alla generazione di 100 coppie di coordinate intere.

generate casualmente e comprese tra (0,0) e (10,10) 2. usando un delimitatore a doppie parentesi
(53–56) (l’iniezione avviene copiando ogni carattere quadre di apertura e chiusura [[ e ]].
di input nel file di output quando il carattere non
sia ‘(’, quindi scrivendo la stringa “(x,y” per poi Nel primo caso la stringa occupa un’unica linea
copiare i rimanenti caratteri della riga a partire da mentre eventuali caratteri di controllo come il ri-
‘)’.). torno a capo \n o la tabulazione \t verranno con-
Il risultato di un’esecuzione su 100 coppie di vertiti nei rispettivi caratteri ascii. Nel secondo
coordinate è dato nella figura 3. caso, all’opposto, i caratteri non stampabili non
verranno interpretati ma è possibile estendere il
8 Lua testo su più linee. Il codice seguente mostra l’uso
di queste due diverse sintassi Lua:
Questo paragrafo e il prossimo, i più corposi per
informazioni ed esempi di codice forniti, mostreran- local s1 = "This is a \\string"
no diverse tecniche basate sulle stringhe di testo o local s2 = 'Literal "\\\\" means "\\"'
sulla tecnologia dei template per generare sorgenti local s3 = [[
TEX. This is a multiline literal string
And this is a strange sequence: \n\t but
8.1 Stringhe in Lua not much...
]]
Le stringhe in Lua sono immutabili, caratteristica
comune ad altri linguaggi. Questo significa che,
print(s1)
una volta creata, una stringa non può più essere print(s2)
modificata. L’unico modo, indiretto, per modificare print(s3)
una stringa è crearne una nuova in sostituzione
dell’originale. Questo programma produce il seguente output:
Esistono sostanzialmente due modi per introdur-
re in un programma Lua valori letterali di tipo This is a \string
stringa: Literal "\\" means "\"
This is a multiline literal string
1. delimitando i caratteri con il simbolo di doppio And this is a strange sequence: \n\t but
apice " oppure con il simbolo di apice semplice not much...
';

50
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Si noti come con il delimitatore a parentesi qua- Possiamo notare che il campo variabile è relativo
dre di Lua venga ignorato l’eventuale primo carat- a un numero in virgola mobile arrotondato alla
tere di ritorno a capo — cosa che succede proprio quinta cifra decimale.
con la stringa s3 dell’esempio formata da quattro I campi variabili, già visti nel paragrafo relativo
linee l’ultima delle quali vuota corrispondente al al C pur senza averne specificato il nome, sono
ritorno a capo inserito dopo not much... delle sequenze formate dal simbolo di percentua-
Infine, volendo inserire nel testo lettera- le e da un carattere che identifica il tipo di dato:
le gli stessi delimitatori, nel caso degli api- tra gli altri, d i numeri interi, s le stringhe, f i
ci è possibile usare ancora l’escaping come in numeri in virgola mobile. Come abbiamo visto è
"una \"bella\" parola", mentre nel caso dei de- possibile inserire tra questi due elementi ulterio-
limitatori a coppie di parentesi quadre è possibile ri specificatori per l’arrotondamento numerico o
usare un qualsiasi numero di segni uguali purché il riempimento di campi a lunghezza fissa. Tutte
bilanciati tra apertura e chiusura come in: queste particolarità — naturalmente documentate
nel manuale di riferimento del linguaggio — de-
[==[una [[bella]] parola]==]
rivano da funzionalità già presenti nel linguaggio
8.2 Creare file di testo C e quindi immediatamente comprensibili per un
In Lua è molto semplice creare un file di testo buon numero di sviluppatori.
ricorrendo alla libreria standard del linguaggio e 8.4 Concatenazione di stringhe
in particolare alle funzioni contenute nel modulo
io. Il metodo io.open() può creare oggetti file La concatenazione di stringhe comporta la crea-
specificando come primo parametro il nome del zione di una nuova stringa pertanto, per motivi di
file stesso e come secondo parametro una delle efficienza e d’utilizzo di memoria, è bene ricorrer-
seguenti modalità: lettura r, scrittura w o scrittura vi per la concatenazione di un numero piccolo di
con aggiunta a. stringhe al di fuori di cicli iterativi. L’operatore
Se il parametro di apertura è la stringa "w" il di unione è rappresentato da due caratteri punto
file verrà aperto con i diritti di scrittura e, se già consecutivi:
esistente, verrà cancellato. Ecco un esempio di co- local s = "Hello" .. " " .. "world!"
dice completo il cui risultato di esecuzione è quello assert( s == "Hello world!")
di creare il più piccolo sorgente TEX possibile:
La costruzione di un sorgente LATEX richiede
-- the most simple source file in TeX
l’unione di molte stringhe perciò è molto vantag-
local filename = "simple.tex"
local content = "Hello world!\n\\bye" gioso introdurre un’altra funzione della libreria
standard chiamata table.concat(). Con essa pos-
-- a new file in the current directory siamo concatenare un numero anche molto grande
-- opened in 'write mode': di stringhe, per esempio per formare una lunga
local file = assert(io.open(filename, "w")) lista di dati.
file:write(content) Il codice di esempio completo, dove una serie
file:close() numerica di venti numeri casuali viene assemblata
Il contenuto del file sarà esattamente il seguente: in un unica stringa, è il seguente:

Hello world! local data = {}


\bye for i=1,20 do
data[#data + 1] = string.format(
Per creare un testo contenente dati variabili "%d = %0.3f",
occorre necessariamente elaborare stringhe utiliz- i,
zando funzioni di libreria o la concatenazione. De- math.random()
dicheremo le prossime due sezioni a ciascuno di )
questi due metodi. end

8.3 Campi variabili local content = table.concat(data, "\n")


print(content)
La funzione della libreria standard di Lua
che produce stringhe con campi variabili è
string.format(). Il primo argomento della fun- 8.5 Considerazioni finali sulle stringhe
zione è una “stringa formato” e gli argomenti suc- Basandoci sulla corrispondenza tra tipo stringa e
cessivi sono i valori corrispondenti ai campi. Questo file di testo abbiamo presentato le tecniche fonda-
breve esempio stampa una riga di codice LATEX in mentali per la costruzione di un sorgente. Queste
modalità matematica: tecniche sono due: la sostituzione di campi formato
local fmt = "\\( \\pi = %0.5f \\)" all’interno delle stringhe costituiti da chiavi segna-
local s = string.format(fmt, math.pi) posto e la concatenazione efficiente di stringhe per
print(s) --> '\( \pi = 3.14159 \)' mezzo di un buffer.

51
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

Figura 4: L’editor SciTE in azione mostra nel pannello laterale l’uscita del programma Lua per generare il codice LATEX
della tabella numerica descritta nel testo. Per eseguire il codice Lua è sufficiente premere il tasto F5 mentre per aprire e
chiudere il pannello di output si usa il tasto F8.

Riassumendo è possibile costruire il testo attra- di seguito, la cui uscita è mostrata nella figura 4,
verso il tipo stringa — tipo immutabile in molti è suddiviso in tre parti:
linguaggi di programmazione — con le seguenti
operazioni: 1. inserimento del codice di apertura dell’ambien-
te tabular e della riga d’intestazione della
• stringhe letterali; tabella;
• campi formato (interpolazione);
2. inserimento tramite un ciclo iterativo delle
• concatenazione efficiente tramite buffering. righe di contenuto numerico;

Nelle prossime sezioni saranno presentati esempi 3. inserimento del codice di chiusura dell’ambien-
concreti che utilizzano le operazioni sulle strin- te tabular.
ghe per generare sorgenti LATEX. Si tratta del-
l’impiego di funzionalità di base dei linguaggi di -- a new table called 't'
programmazione o delle loro librerie standard. local t = {[[\begin{tabular}{crrrr}]]}
t[#t+1] = [[\toprule]]
8.6 Come eseguire i programmi Lua t[#t+1] = [[\(x\) & \(x^2\) & \(x^3\)]]
L’esecuzione di uno script Lua avviene allo stesso ..[[ & \(x^4\) & \(x^5\)\\]]
modo della compilazione di un sorgente LATEX: da t[#t+1] = [[\midrule]]
riga di comando oppure da un editor predisposto
tramite pressione di una combinazione di tasti. local f = [[ %2d & %3d & %4d & %5d & %6d\\]]
for x = 1,10 do
Se non si vuole installare un interprete Lua si
local line = string.format(
può far ricorso al programma texlua compreso in f, x, x^2, x^3, x^4, x^5
TEX Live, mentre come editor è consigliabile l’uti- )
lizzo di SciTE (figura 4), in grado di visualizzare t[#t+1] = line
in un pannello laterale l’uscita dei programmi lan- end
ciati premendo il tasto F5. L’editor SciTE è molto
comodo e può essere programmato internamente t[#t+1] = [[\bottomrule]]
in Lua. t[#t+1] = [[\end{tabular}]]

8.7 Un esempio: costruire una tabella print(table.concat(t, "\n"))


Ci proponiamo di costruire il codice LATEX cor-
rispondente a una tabella numerica in cui nella Lo scopo di questo esempio non è presentare
prima colonna compare una variabile indipendente una soluzione definitiva — anzi il codice può essere
e nelle successive i suoi corrispondenti valori delle migliorato in molti modi — ma di mostrare il mo-
potenze fino alla quinta. dello d’implementazione basato sul tipo stringa per
Ci avvarremo di una tabella Lua dove memo- la produzione automatica di codice: una sequenza
rizzare riga per riga tutto il codice LATEX per poi d’inserimenti di testo — stringa letterale o dato
stampare il risultato in console. Il codice mostrato variabile — in un buffer, linea per linea.

52
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

8.8 lustache un template engine per Lua "The LaTeX Graphics Companion",
"The LaTeX Web Companion",
lustache è un’implemetazione in Lua del sistema
}
di template mustache. Si tratta di specifiche gene-
rali consultabili alla pagina (Mustache, 2015) doc = lustache:render(
il cui nome fa riferimento alla somiglianza tra tmpl,
le parentesi graffe dei delimitatori e un bel pa- {list = book_list}
io di baffi. Tali specifiche sono disponibili in un )
buon numero di linguaggi grazie a progetti indipen- print(doc)
denti. Rimandiamo il lettore al sito della libreria
Il tag di blocco list è composto dal tag di
lustache (Olivine-Labs, 2015) per le istruzioni
apertura #list e da quello di chiusura /list. Al-
d’installazione.
l’interno di questo blocco si trova un tag anonimo,
Un template per un sorgente TEX minimo è
rappresentato dal carattere ‘.’, che riceverà uno
mostrato nel seguente frammento di codice:
alla volta gli elementi contenuti nella tabella/array
local template = [[ list.
Hello {{ my_friend }}! L’uscita del programma, riportata di seguito,
{{ bye }}]] mostra alcune imperfezioni:
Per l’esecuzione di questo template occorre for- \begin{itemize}
nire al metodo render() della libreria lustache
\item Guide to LaTeX;
una tabella con chiavi uguali a my_friend e bye
previste dai due tag. I valori associati a queste \item The LaTeX Companion;
chiavi verranno inseriti nel testo finale. Se invece
la chiave non è presente, il tag in questione verrà \item The LaTeX graphics Companion;
semplicemente ignorato.
Il codice d’esempio completo è il seguente: \item The LaTeX Web Companion;

local lustache = require "lustache" \end{itemize}

local template = [[ 8.10 Controllare i ritorni a capo


Hello {{ my_friend }}!
Eliminare dal template i caratteri di fine riga com-
{{ bye }}]]
porta la loro eliminazione anche dal testo finale;
local data = { nell’esempio erano stati introdotti per maggiore
my_friend = "John Doe", chiarezza del codice del template. Diversamente
bye = "\\bye", possiamo usare i tag commento — tali quando il
} carattere ‘!’ segue i delimitatori di apertura — per-
ché ammettono ritorni a capo al loro interno. Ecco
local doc = lustache:render(template, data) come potremmo riscrivere il template precedente
print(doc) -- or save it on a .tex file per eliminare le righe bianche di troppo:
e troveremo in console il testo sperato: -- no newline
local tmpl = [[
Hello John Doe! \begin{itemize}
\bye {{#list}} \item {{ . }};
{{/list}}\end{itemize}]]
8.9 Una lista
Forma alternative sono:
Oltre ai tag semplici ci sono quelli di blocco concet-
tualmente simili agli ambienti di LATEX. Per iterare -- newline with comment tag
il testo di una lista, per esempio, si può creare un local tmpl = [[
blocco dopodiché una lista puntata: \begin{itemize}
{{#list}}{{! comment tag
local lustache = require "lustache" }} \item {{ . }};
{{/list}}{{!
local tmpl = [[ }}\end{itemize}]]
\begin{itemize}
oppure:
{{#list}}
\item {{ . }}; -- with tag inner newline
{{/list}} local tmpl = [[
\end{itemize}]] \begin{itemize}
{{#list
local book_list = { }} \item {{ . }};
"Guide to LaTeX", {{/list
"The LaTeX Companion", }}\end{itemize}]]

53
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

8.11 Generalizzare il tipo di lista local lustache = require "lustache"


Il modo più semplice per generalizzare il tipo di
local tmpl = [[
lista è renderla attraverso un tag. Ciò implica modi- \begin{itemize}
ficare i delimitatori a coppie di graffe a causa delle {{#list}} \item {{ book }}{{!
possibili interazioni con le graffe che circondano }}{{^dot}};{{/dot}}{{#dot}}.{{/dot}}
il nome dell’ambiente, come dimostra il seguente {{/list}}\end{itemize}]]
template:
local list = {
local tmpl = [[ {book = "Guide to LaTeX"},
\begin{{{ env }}} {book = "The LaTeX Companion"},
{{#list}} \item {{ . }}; {book = "The LaTeX graphics Companion"},
{{/list}}\end{ {{ env }} }]] {book = "The LaTeX Web Companion",
dot = true},
Il primo tag verrà interpretato come un tag }
delimitato a tre graffe che a differenza di quel-
lo a due evita l’escaping del contenuto — ne ri- doc = lustache:render(tmpl, {list=list})
parleremo più avanti nella sezione 8.15 — dando print(doc)
\beginitemize mentre l’ultimo tag darà come ci
si aspetta \end{ itemize }. Nel template sono stati inseriti due nuovi blocchi;
Non è facile trovare dei delimitatori che certa- il primo sarà eseguito per effetto del carattere ^ se
mente non compariranno mai in un sorgente TEX, la chiave dot non esiste, viceversa il secondo. Così,
che siano compatibili con la sintassi Lua per le inserendo solamente nell’ultima tabella il campo
stringhe e che rendano il template il più leggibile dot, otteniamo l’effetto di aggiungere il ; alla fine
possibile. In questo esempio abbiamo pensato alla di tutte le righe tranne l’ultima dove troveremo il
coppia <| |>. I delimitatori vengono modificati, carattere punto.
direttamente nel template e ogni volta che sarà ne- Modificare la struttura dati per ottemperare a
cessario, tramite un tag e il segno =, carattere che difetti del template è un rimedio estremo che molto
non può essere compreso nei delimitatori assieme probabilmente compromette lo sviluppo del pro-
al carattere spazio. getto proprio perché l’obiettivo primario è quello
Nell’esempio di codice completo è possibile no- di separare i dati dal modello del testo.
tare che i segni di uguale = sono esterni ai nuovi Per fortuna è possibile avvalerci di alcune funzio-
delimitatori e che questi devono essere separati nalità avanzate di Lua come le closure e le funzioni
almeno da un carattere spazio: come oggetti di prima classe. Nel codice che segue
il problema è risolto senza modificare i dati: una
local lustache = require "lustache" funzione è inserita nel template e sulla base del
numero di riga aggiunge il corretto carattere finale:
local tmpl = [[{{=<| |>=}}<|!
|>\begin{<| env |>} local lustache = require "lustache"
<|#list|> \item <| . |>;
<|/list|>\end{<| env |>}]] local tmpl = [[
\begin{itemize}
local books = { {{#list
"Guide to LaTeX", }} \item {{ . }}{{ end_char }}
"The LaTeX Companion", {{/list
"The LaTeX Graphics Companion", }}\end{itemize}]]
"The LaTeX Web Companion",
} local book_list = {
"Guide to LaTeX",
doc = lustache:render(tmpl, "The LaTeX Companion",
{list = books, env="itemize"}) "The LaTeX Graphics Companion",
print(doc) "The LaTeX Web Companion",
}
8.12 Rendere speciale l’ultimo elemento
Le specifiche mustache non prevedono la gestione local rows = #book_list
speciale dell’ultimo elemento della lista, per esem- -- closure
function end_char()
pio per inserire il carattere ‘.’ finale al posto del
rows = rows - 1
punto e virgola.
if rows == 0 then
Nel caso in cui la chiave prevista dal tag non è return "."
presente nei dati, non verrà generato un errore ma else
semplicemente il testo di sostituzione sarà vuoto. return ";"
Questo comportamento ci consente di risolvere il end
problema al prezzo di una modifica dei dati: end

54
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Tabella 2: La tabella ricavata dal codice Lua riportato nel


testo che fa uso della tecnologia dei template implementata
doc = lustache:render(tmpl, nella libreria lustache.
{list = book_list,
end_char = end_char,}) x x2 x3 x4 x5
print(doc)
1 1 1 1 1
8.13 Una tabella numerica 2 4 8 16 32
3 9 27 81 243
Volendo replicare l’esempio della tabella numeri-
4 16 64 256 1024
ca della sezione 8.7 possiamo scrivere il seguente
5 25 125 625 3125
template: 6 36 216 1296 7776
local lustache = require "lustache" 7 49 343 2401 16807
8 64 512 4096 32768
local tmpl = [[ 9 81 729 6561 59049
\begin{tabular}{crrrr} 10 100 1000 10000 100000
\toprule{{! package booktabs required }}
\( x \) & \( x^2 \) & \( x^3 \){{!
}} & \( x^4 \) & \( x^5 \)\\ local tmpl = [[
\midrule \begin{tabular}{crrrr}
{{#rows \toprule{{! package booktabs required }}
}}{{x1}} & {{x2}} & {{x3}}{{! \( x \) & \( x^2 \) & \( x^3 \){{!
}} & {{x4}} & {{x5}}\\ }} & \( x^4 \) & \( x^5 \)\\
{{/rows \midrule
}}\bottomrule {{#rows
\end{tabular}]] }}{{x1}} & {{x2}} & {{x3}}{{!
}} & {{x4}} & {{x5}}\\
local rows = {} {{/rows
for x = 1,10 do }}\bottomrule
rows[#rows+1] = { \end{tabular}]]
x1 = x,
x2 = x^2, local rows = {}
x3 = x^3, for i=1,10 do
x4 = x^4, rows[#rows+1] = {x=i}
x5 = x^5, end
}
end local f = string.format
local data = {rows=rows} local data = {
local doc = lustache:render(tmpl, data) rows = rows,
print(doc) x1=function (t) return f("%2d", t.x ) end,
x2=function (t) return f("%3d", t.x^2) end,
Anche in questo caso le caratteristiche avanzate x3=function (t) return f("%4d", t.x^3) end,
di Lua e della libreria lustache possono essere x4=function (t) return f("%5d", t.x^4) end,
impiegate per offrire una soluzione più elegante va- x5=function (t) return f("%6d", t.x^5) end,
riando il dato associato ai nomi x1, . . . x5 del tem- }
plate: anziché dati numerici è possibile associare
loro delle funzioni. local doc = lustache:render(tmpl, data)
Questa volta la tabella con i dati per il tem- print(doc)
plate, chiamata data, conterrà alla chiave rows
8.14 Una funzione come tag di un
una tabella/array contenente la serie di tabelle con
template
chiave x. Tale serie verrà passata in sequenza alle
funzioni, e quindi alle chiavi x1, . . . x5, le funzioni La libreria lustache ammette che i tag di blocco
di calcolo. possano corrispondere a funzioni. Il testo risultante
È semplice approfittarne di queste funzioni sarà il valore restituito dalla funzione associata al
per aggiungere spazi bianchi in numero suffi- tag.
ciente per incolonnare a destra i valori; a ta- Cominciamo col mostrare il funzionamento di
le proposito utilizzeremo la funzione di libreria questa tecnica scrivendo una funzione che restitui-
string.format(). sce il contenuto del blocco racchiuso in una ma-
Il listato seguente implementa quanto detto uti- cro LATEX (il primo argomento tabella può essere
lizzando lo stesso template del codice precedente. ignorato):
Il risultato del codice LATEX compilato è riportato
nella tabella 2: local lustache = require "lustache"

local lustache = require "lustache" local tmpl = [[


{{#bf}}a great idea{{/bf}}]]

55
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

}
local doc = lustache:render( )
tmpl, print(doc)
{bf = function (tab, rendered)
return "\\textbf{"..rendered.."}" 8.15 Templating ricorsivo
end In precedenza abbiamo accennato al fatto che un
}
template può contenerne altri per modellare un
)
print(doc)
testo strutturato. La struttura testuale espressa
per mezzo di template ricorsivi è molto potente
Il risultato è il seguente: ma può risultare complessa da gestire.
Nei termini della libreria lustache un sub-
\textbf{a great idea}
template viene chiamato partials. Esso è un norma-
Il tag funzione può contenere ulteriori tag, per le template i cui tag devono però far riferimento
esempio: diretto ai dati forniti al template principale.
Nel template di livello superiore invece, un ap-
local lustache = require "lustache"
posito tag caratterizzato dal segno di maggiore
(>) inserito subito dopo il delimitatore di apertura
local tmpl = [[
{{#bf}}a great {{ thing }}{{/bf}}]] espanderà il template figlio. Quest’ultimo dovrà
essere contenuto in una tabella passata come terzo
local function enclose_bf(tab, rendered) argomento alla funzione render().
return "\\textbf{"..rendered.."}" La costruzione del codice LATEX per inserire
end una figura come oggetto mobile è un esempio
opportuno:
local doc = lustache:render(
tmpl, \begin{figure}
{thing = "song", bf = enclose_bf} \centering
) \includegraphics[width=\textwidth]{image}
print(doc) --> "\textbf{a great song}" \caption{A great image.}
\label{fig:animage}
Questa funzionalità potrebbe essere sfruttata \end{figure}
per formattare numeri decimali poiché, nella sua
essenzialità, la libreria lustache non offre la pos- Per generare questo testo definiamo un template
sibilità di applicare un formato ai dati. L’idea di principale che esprime il modello generale di un
per sé è corretta perché deve essere il template il ambiente LATEX contenente un secondo template
componente responsabile dell’aspetto da far assu- per il contenuto. Modificando al solito i delimitatori
mere ai dati (principio di separazione tra modello di default avremo:
e vista).
local tmpl_env = [[
Nel prossimo esempio di codice questo concetto {{=<| |>=}}\begin{<| env |>}
è implementato con una funzione generale chiama- <|> body |>\end{<| env |>}]]
ta format() perché sia semplice creare ulteriori
funzioni di formato come la num3(): Il template interno accoglierà i due campi varia-
bili opzionali per la posizione centrata dell’imma-
local lustache = require "lustache"
gine e la larghezza imposta e i tre campi obbliga-
local tmpl = [[
tori per il nome del file, il testo della didascalia e
\( \pi = {{#num3}}{{pi}}{{/num3}}\). l’etichetta di riferimento.
\( e = {{#num3}}2.718281828459{{/num3}}\). Il fatto che un dato opzionale può non esistere si
]] riflette nel modo in cui il tag associato è inserito nel
template, cosa che lo rende meno leggibile, come è
local function format(x, prec) possibile constatare nel seguente script (la cosa è
local fmt = string.format( specialmente vera per la prima parte del template
"%%0.%df", interno):
prec
) local lustache = require "lustache"
return string.format(fmt, x)
end local tmpl_env = [[
{{=<| |>=}}\begin{<| env |>}
local doc = lustache:render( <|> body |>\end{<| env |>}]]
tmpl,
{pi = math.pi, local partials = {body = [[
num3 = function (t, x) {{#center}}\centering
return format(x, 3) {{/center}}\includegraphics{{#width
end }}[width={{width}}]{{/width}}{{=<|

56
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Poiché la definizione dello stile fa uso del carat-


tere slash occorre cambiare il comportamento della
libreria lustache per l’escaping html che trasfor-
merebbe il carattere / nella sequenza &#x2F;. La
modifica si imposta nel tag inserendo tre parente-
si graffe di delimitazione oppure premettendo al
nome del campo contenuto il carattere &:
local tmpl_opt = [[
[{{ &style_def }}]
]]

Il secondo template, body, contiene l’istruzione


per il disegno della circonferenza di raggio dipen-
dente da un parametro e l’inserimento di istruzioni
\node nelle coordinate casuali del piano x e y:
local tmpl_body = [[
\draw[dotstyle] (0,0) circle{{!
}} [radius={{ radius }}cm];
Figura 5: Una figura in TikZ il cui codice sorgente è stato {{#node_list}}{{!
generato da un programma che utilizza la tecnologia dei }} \node[dotstyle] at ({{x}}, {{y}}) {};
template annidati, scalata rispetto alle dimensioni originali. {{/node_list}}]]

Il codice completo dello script Lua è il seguente:


|>=}}{<| file |>}
\caption{<| caption |>} local lustache = require "lustache"
\label{<| label |>}
]]} local tmpl_env = [[{{=<% %>=}}<%!
%>\begin{<% env %>}<%> option %><%!
doc = lustache:render( %><%> body %><%!
tmpl_env, %>\end{<% env %>}]]
{
env="figure", local tmpl_opt = [[
center = true, [{{ &style_def }}]
width = "\\textwidth", ]]
file = "image",
caption = "A great image.", local style_def = [[
label = "fig:animage", dotstyle/.style={
}, circle, % the shape
partials minimum size=5mm, % the size
) % the border:
print(doc) very thick,
draw=red!50!black!50,
Un’ulteriore discussione sui template annidati % the filling:
è quella che riguarda la realizzazione del codice top color=white,
che produce la figura 5 con l’utilizzo del pacchetto bottom color=red!50!black!20
grafico TikZ (Tantau, 2015). Si tratta di disporre }]]
in modo casuale 24 cerchietti su una circonferenza.
La struttura che desideriamo dare al template local tmpl_body = [[
principale corrisponde a un ambiente con opzioni; \draw[dotstyle] (0,0) circle{{!
il modello realizzato comprende due sottostrutture }} [radius={{radius}}cm];
a cui corrisponderanno altrettanti template interni {{#node_list}}{{!
}} \node[dotstyle] at ({{x}}, {{y}}) {};
chiamati rispettivamente option e body:
{{/node_list}}]]
local tmpl_env = [[
{{=<% %>=}}\begin{<% env %>}<%> option %> local radius = 4 -- cm
<%> body %> local n = 24
\end{<% env %>}]] local node = {}
for i=1,n do
Il primo sub-template, option, è semplice: con- local alpha = 2*math.pi*math.random()
tiene le parentesi quadre che racchiudono un tag node[#node+1] = {
associato alla stringa completa il cui codice defini- x = radius*math.cos(alpha),
sce lo stile TikZ dei cerchietti. Indubbiamente una y = radius*math.sin(alpha),
soluzione che potrà essere sostituita da un’altra }
più sofisticata. end

57
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

• valutazione diretta di espressioni letterali;


doc = lustache:render(
tmpl_env, -- main template • possibilità d’inserire dei commenti;
{ -- data
env="tikzpicture", • direttamente interpretabile da Lua senza al-
style_def = style_def, cuna necessità di realizzare un parser per
node_list = node, l’elaborazione successiva.
radius = radius, -- cm
}, La struttura può essere disegnata per esprime-
{-- sub template
re quella del problema nei termini in cui l’uten-
option = tmpl_opt,
body = tmpl_body,
te pensa alle informazioni. Tornando all’esempio
} — confronta la riga 3 della tabella 3 — il fatto che
) una persona di nome Sherri Castro ha svolto l’atti-
print(doc) vità A nel 1972 e dal 1974 al 1976 potrebbe essere
espresso scrivendo:
I parametri principali come il raggio della circon-
ferenza e il numero dei cerchietti sono memorizzati name: Sherri Castro, A: 1972, 1974->1976
in variabili nella memoria locale del programma.
Anziché con un unico template, il modello di docu- In lua la tabella corrispondente alla sintassi
mento è rappresentato con template specializzati letterale del costruttore sarebbe:
per la struttura testuale associata.
{name="Sherri Castro", A={1972,{1974,1976}}}

9 Primo caso d’uso: griglia di che a parole è descritta come una tabella con due
attività chiavi, una per il nome e una per l’attività a cui
è associata una seconda tabella che elenca anni e
Questo esempio mostrerà come generare il sorgen-
intervalli di anni (a loro volta tabelle).
te LATEX per comporre una tabella le cui righe
Siamo liberi di indentare questa struttura per
rappresentano lo svolgimento di attività annuali
rappresentarla più chiaramente così:
di un gruppo di persone. Ogni cella della tabella,
individuata da una colonna relativa agli anni e da { name = "Sherri Castro",
una riga relativa alla persona, conterrà una barra A = { 1972,
il cui colore rappresenterà un’attività. La tabella 3 {1974,1976}, -- triennio
mostra un’istanza specifica di tale griglia. }
L’insieme dei dati reali — fornito da France- }
sco Endrici — è stato opportunamente modificato
sostituendo nomi e definizioni senza per questo Modifichiamo la tabella nella versione defini-
alterare la struttura del problema. Per tradurli tiva per poter assegnare al membro del gruppo
nella griglia utilizzeremo Lua e le sue tecniche di lo svolgimento di più attività: basta introdurre
elaborazione delle stringhe sfruttando il costrut- una tabella per un ulteriore livello di annidamento
tore delle tabelle — l’unica struttura dati di base che associamo alla chiave task. In questo modo
prevista nel linguaggio — come sintassi formale si rimuove anche la limitazione della precedente
per descrivere le informazioni. struttura per cui il codice delle attività non poteva
essere identificato con la chiave name:
9.1 Data description
{ name = "Helen Austin",
Una tabella di Lua è un dizionario, una struttura
task = {-- cfr. riga 6 tabella
dati formata da chiavi univoche a cui è associato A = {1977, {1983, 1985}},
un valore che può essere a sua volta una tabella. B = {{1973,1976}, 1981}
Questo tipo non solo è un contenitore molto fles- }
sibile ma ammette una sintassi letterale ispirata }
a BibTEX dove le chiavi stringa compaiono sen-
za delimitatori. Questa particolarità consente al 9.2 Caricamento dei dati
costruttore di tabelle di Lua di essere utilizzato
Abbiamo due modalità per leggere le tabelle Lua:
anche come formato di descrizione dati strutturato
la prima è quella d’inserire i dati in un file do-
ed espressivo. Un file di testo prodotto dall’uten-
ve un’istruzione return è seguita da una tabella
te tramite un editor può diventare così una sorta
che contiene tutte le altre e poi caricare il file nel-
d’archivio con i seguenti vantaggi:
lo script tramite la funzione predefinita dofile()
• semplice e immediata modalità d’inserimento che ne interpreta il contenuto come codice Lua. La
e modifica dei dati da parte dell’utente; seconda è premettere alle definizioni di ciascuna
tabella un nome. Questa sintassi verrà interpreta-
• livello arbitrario di complessità; ta da Lua come la richiesta dell’esecuzione della

58
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Tabella 3: Porzione della tabella di grandi dimensioni che rappresenta il grafico dello svolgimento di varie attività annuali
di un gruppo di persone, prodotta elaborando un file di testo contenente i dati espressi nel formato di data description di
Lua. La tabella originale comprende le attività di circa 160 persone nell’arco di tempo di circa 65 anni.

1972 1973 1974 1975 1976 1977 1978 1981 1983 1984 1985
Lisa Green Lisa Green
Carla Figueroa Carla Figueroa
Sherri Castro Sherri Castro
Faye Ramsey Faye Ramsey
Greg Frazier Greg Frazier
Helen Austin Helen Austin

funzione con lo stesso nome e con unico argomen- La virgola dopo l’ultimo elemento di una tabella
to la tabella stessa.13 Nello script dovremo prima è opzionale; inserirla facilita l’aggiunta di eventuali
definire la funzione e poi caricare il file dati ancora altri elementi, perciò considereremo il suo inseri-
tramite dofile(). mento una buona prassi. Nel codice precedente
Poiché la sua sintassi è più significativa, useremo sarebbe anche preferibile, per lo stesso motivo, de-
questo secondo modo. Il seguente è un frammento dicare una riga per ciascun anno o per ciascun
del file dati a cui corrisponde la tabella 3 in cui la intervallo di anni d’attività.
funzione è stata chiamata service():
9.3 Il codice di lettura
service{name = "Lisa Green", Con la sola funzione service() non è possibile
task = { costruire il codice LATEX della riga di griglia. Per
ASCI = {{1972,1974},}, farlo è necessario conoscere tutti gli anni di colonna
EG = {1975, {1977,1978},}, per poter inserire il numero corretto di separatori
} di cella. Il compito di questa funzione sarà allora
} quello di memorizzare i dati in alcune variabili
globali per la successiva elaborazione d’insieme.
service{name = "Carla Figueroa",
task = {
Per semplicità abbiamo scelto di non includere
AsE = {1972,}, nel codice controlli di validità, per esempio per
} avvertire l’utente che nei dati relativi a una stessa
} attività un anno è stato specificato più volte o
che per uno stesso anno la stessa persona ha più
service{name = "Sherri Castro", attività.
task = { Poiché gli anni possono essere espressi come
ASCI = {1972,1974,}, valori singoli o come intervalli, introduciamo la
EG = {{1975,1976},}, funzione ausiliaria expand_year() che linearizza
} gli intervalli e restituisce l’array dei valori sem-
}
plici. La funzione service() mapperà gli anni in
service{name = "Faye Ramsey",
un dizionario a valori booleani per tener traccia
task = { delle colonne della griglia e aggiungerà una riga
ASCI = {{1973,1974},}, associando gli anni con le attività compiute dalla
} singola persona:
}
-- data section
service{name = "Greg Frazier",
local year_map = {}
task = {
local row_data = {}
AsE = {{1973,1976},},
}
-- expand the years tab in a plain array
}
local function expand_year(t_acty)
local y_list = {}
service{name = "Helen Austin",
for _, elem in ipairs(t_acty) do
task = {
if type(elem) == "table" then
EG = {1977, {1983,1985},},
for y = elem[1], elem[2] do
LC = {{1973,1976},1981,},
y_list[#y_list+1] = y
}
end
}
else
13. In Lua le parentesi tonde della chiamata di funzione y_list[#y_list+1] = elem
possono essere omesse se l’argomento è unico e se questo è end
di tipo stringa oppure di tipo tabella. end

59
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

return y_list un file contenente un ambiente tabular che sarà


end a sua volta inserito in un sorgente LATEX princi-
pale, basato sulla classe standalone, che definirà i
function service(t) colori, le dimensioni, i font, le macro e caricherà i
local row = {name = t.name, years = {}} pacchetti necessari.
for acty, t_years in pairs(t.task) do
In particolare, faremo uso del pacchetto array per
for _, y in
ipairs(expand_year(t_years)) do
ottenere colonne centrate e della stessa larghezza,
-- mapping global columns il pacchetto booktabs per i filetti orizzontali della
year_map[y] = year_map[y] or true prima e dell’ultima riga, il pacchetto xcolor per
-- insert activity definire i colori delle barre nelle celle e il pacchetto
row[y] = acty colortbl per assegnare il colore dei filetti.
end Il contenuto delle celle non vuote sarà una bar-
end ra colorata orizzontale prodotta da una macro il
row_data[#row_data+1] = row cui nome corrisponderà a quello dei codici identi-
end ficativi delle attività usati nei dati. Per esempio,
L’effetto di queste definizioni e la conseguente all’interno del sorgente principale la definizione del-
esecuzione del seguente frammento di codice scritto le grandezze dimensionali in sintassi LATEX comuni
dall’utente: a tutte le barre, la definizione del colore e della
macro per l’attività “EG” sarà:
service{name = "Helen Austin",
task = { % definition of the bar colors
EG = {1977, {1983,1985},}, ...
LC = {{1973,1976},1981,}, \definecolor{dep}{rgb}{0.24,0.55,0.20}
} ...
}
% column width: 1.5 the length of ``2012''
sarà l’aggiunta in coda alle altre righe della se- \newlength{\tcellwidth}
guente tabella Lua contenente il nome che andrà \settowidth{\tcellwidth}{2012}
posizionato nella prima e nell’ultima colonna della \setlength{\tcellwidth}{1.5\tcellwidth}
griglia e anno per anno tutte le attività:
% definition of the bar dimensions
{name = "Helen Austin", \newlength{\tbarheight}
years = { \setlength{\tbarheight}{5pt}
[1977] = "EG", \newlength{\tbarwidth}
[1983] = "EG", \setlength{\tbarwidth}{0.97\tcellwidth}
[1984] = "EG", \newlength{\tbarup}
[1985] = "EG", \setlength{\tbarup}{0.5pt}
[1973] = "LC",
[1974] = "LC", % the bar 'factory' command
[1975] = "LC", \newcommand{\servicebar}[1]{{%
[1976] = "LC", \color{#1}%
[1981] = "LC", \rule[\tbarup]{\tbarwidth}{\tbarheight}%
}} }}

La mappa globale degli anni/colonna aggiorna- % the bar commands


ta per ogni esecuzione dalla funzione service() ...
dovrà integrare questi dati. Nella prossima sezione \newcommand{\EG}{\servicebar{dep}}
vedremo infatti come la funzione render_data() ...
tradurrà questa stessa tabella Lua per Helen Au-
Tornando a Lua, la funzione render_data() uti-
stin in una riga della tabella 3 nel seguente codice
lizzerà la mappa degli anni/colonna per generare
LATEX qui suddiviso su due linee:
la definizione delle colonne, parametro obbligatorio
Helen Austin & & \LC & \LC & \LC & \LC & \EG dell’ambiente tabular, e la prima riga d’intestazione,
& & \LC & \EG & \EG & \EG & Helen Austin\\ mentre utilizzerà i dati delle righe per generare la
sequenza di celle separate dal carattere ‘&’. Ancora
9.4 Il codice di elaborazione una volta il processo memorizzerà l’intero ambien-
Al termine dell’esecuzione di tutte le funzioni te tabular riga per riga in una tabella Lua che verrà
service() contenute nel file dati, saranno dispo- poi unita nel testo finale in modo efficiente con la
nibili in memoria tutte le informazioni necessarie funzione predefinita table.concat().
alla creazione della griglia in due tabelle Lua: la Il codice completo è riportato di seguito. Esso
mappa degli anni di attività per tutte le persone e non è né semplice né elegante perché disseminato
l’array con i dati delle righe. Passando le relative di stringhe di codice LATEX che lo rendono poco
variabili alla funzione render_data() produrremo leggibile. Utilizzare le tecniche basate sulle stringhe

60
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

era del resto una scelta iniziale del progetto ma rs[#rs+1] = "\\bottomrule"
lasciamo al lettore per utile esercizio riscrivere il rs[#rs+1] = "\\end{tabular}"
codice questa volta utilizzando il template engine return rs
lustache. end

-- rendering data section 9.5 Il codice di scrittura


Non rimane che completare lo script scrivendo il
function render_data(t_years, t_data)
codice che carica il file dati, esegue la funzione di
-- ordered list of column years
traduzione nel codice LATEX e salva il risultato in
local col_years = {}
for y in pairs(t_years) do un file esterno:
col_years[#col_years+1] = y -- execution section
end
table.sort(col_years) dofile("services.lua")
local t = render_data(year_map, row_data)
local rs = {} -- save the LaTeX file
-- tabular header local tex = io.open("tabdata.tex", "w")
local cdef = "r@{\\hspace{0.5em}}".. tex:write(table.concat(t,"\n"))
"*{%d}{@{}C{\\tcellwidth}@{}}".. tex:close()
"@{\\hspace{0.5em}}l"
rs[#rs+1] = string.format( 9.6 Considerazioni finali sulla griglia
"\\begin{tabular}{%s}",
string.format(cdef, #col_years) Rispetto alla soluzione originale scritta in LATEX3
) quella presentata qui in Lua non è tanto più effi-
rs[#rs+1] = "\\toprule" ciente quanto è più leggibile e robusta e ciò porta a
local colfmt = {} maggiori potenzialità di miglioramento per effetto
for _, y in ipairs(col_years) do dell’uso di un linguaggio ad alto livello progettato
colfmt[#colfmt+1] = string.format( espressamente anche per questi compiti.
"\\textbf{%d}", Per esempio, non sarebbe difficile modificare il
y
programma per considerare anche il caso in cui
)
una persona svolga più attività in uno stesso anno,
end
rs[#rs+1] = "& ".. oppure per eseguire l’ordinamento temporale delle
table.concat(colfmt, " & ").. righe della griglia evitando all’utente l’obbligo di
" &\\\\" inserire in sequenza le tabelle dati service, oppure
rs[#rs+1] = "\\midrule" per creare griglie finestra selezionando un intervallo
di anni.
-- tabular body Mentre il formato di dati originale è un file .csv
for i, row in ipairs(t_data) do (comma separated values) proveniente da un foglio
local rndrow = {} di calcolo, in questo esempio applicativo le stesse
for _, year in ipairs(col_years) do informazioni sono state tradotte in un formato di
if row[year] then
data description direttamente interpretabile da Lua
rndrow[#rndrow+1] = "\\"..
row[year]
sfruttando la flessibile sintassi del suo costruttore
else di tabelle. Con esso è stata progettata una strut-
rndrow[#rndrow+1] = " " tura espressiva pensando alle informazioni inerenti
end allo svolgimento di attività annuali da parte di
end componenti di un gruppo che l’utente può inserire
local hline manualmente.
if i == #t_data then
hline = "" 10 Python
else
hline = "\\hline" L’ultimo linguaggio considerato in questo lavoro
end è Python, diffusissimo linguaggio di scripting per
molto tempo contrapposto a Perl.
rs[#rs+1] = row.name..
" & ".. 10.1 Stringhe e campi
table.concat(rndrow, " & ")..
In Python3 le stringhe sono codificate Unicode
" & "..
row.name..
utf-8 perciò preferiamo questa versione — in parti-
"\\\\".. colare la 3.4 — rispetto a quella della precedente
hline serie stabile. Al pari di Lua, il linguaggio prevede
end diverse sintassi per inserire valori stringa letterali
usando due diversi delimitatori: gli apici semplici
-- tabular ending o doppi per le stringhe di un’unica riga oppure tre

61
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

apici semplici o doppi consecutivi per le stringhe La prima differenza che incontriamo rispetto a
multiriga. Premettendo il modificatore r (raw) si lustache è che i tag dei template non hanno un
disinseriscono le sequenze di escape, cosa utile, per carattere marcatore che ne specifica il tipo — come
esempio, nei casi di stringhe contenenti il carattere il # per un blocco o il ! per un commento — ma
backslash delle macro TEX. delimitatori diversi per ciascun tipo. I tipi di tag
Nelle prossime sezioni è riportato del codice che in Jinja sono quattro e sono qui elencati con i
fa uso della sintassi per le stringhe grezze sia mono delimitatori di default:
che multiriga e della funzione format() operante
come metodo del tipo stringa per testi con campi • {% . . . %} istruzioni
variabili. • {{ . . . }} espressioni valutate e inserite nel
10.2 Costruire una tabella testo finale,
Diversamente dagli esempi precedenti, nel prossimo • {# . . . #} commenti non inclusi nell’uscita del
script il testo LATEX della tabella numerica già template,
realizzata in Lua non verrà stampato a video sul
canale di uscita ma sarà memorizzato in un file • # . . . ## istruzioni in linea.
su disco. Questo ci darà modo di presentare le
istruzioni necessarie per accedere al file system. 10.4 Merging PDF file

# creating a new list also called 't'


Come primo esempio con il modulo Jinja stu-
t = [r"\begin{tabular}{crrrr}"] dieremo la creazione di un file sorgente LATEX
t.append(r"\toprule") per l’unione di più file pdf tramite il pacchetto
t.append(r"\(x\) & \(x^2\) & \(x^3\)" pdfpages.
r" & \(x^4\) & \(x^5\)\\") Nel template troveremo un tag istruzione per un
t.append(r"\midrule") ciclo iterativo che genererà la sequenza di macro
\includepdf con una sintassi molto simile a quella
f = (r" {0:2d} & {1:3d} & {2:4d} &" in Python. La variabile d’iterazione è inserita in
r" {3:5d} & {4:6d}\\") un tag espressione con delimitatori personalizzati:
for x in range(1,11):
t.append( tmpl = r"""
f.format(x, x**2, x**3, \documentclass{minimal}
x**4, x**5 \usepackage{pdfpages}
)) \begin{document}
{% for doc in doc_list -%}
t.append(r"\bottomrule") \includepdf[pages=-]{<<| doc |>>}
t.append(r"\end{tabular}") {% endfor -%}
\end{document}
# saving content in the TeX file """
fl = open("test-table.tex", "w")
fl.write("\n".join(t)) Il template è molto più leggibile che non l’e-
fl.close() quivalente per lustache perché possiamo inserire
prima del tag di chiusura il modificatore trattino -,
Nonostante le molte differenze concettuali tra
che comporta l’eliminazione dei caratteri spazio
Lua e Python, i due programmi per produrre la
e di ritorno a capo dal testo finale successivi al
tabella numerica d’esempio sono molto simili. Nel
delimitatore di chiusura del tag. Questo compor-
caso di Python si usa una lista allo stesso modo
tamento può essere reso implicito configurando
della tabella/array in Lua, il metodo format() al
opportunamente gli oggetti restituiti dal modulo.
posto della funzione string.format() e il metodo
Veniamo all’esame del codice dello script: per
join() al posto della funzione table.concat().
prima cosa si carica l’oggetto Environment del
La figura 6 mostra l’editor idle, dedicato alla
modulo jinja2 istanziandone l’oggetto con il co-
programmazione in Python, al lavoro sul codice
struttore a cui vengono passate le nuove stringhe di
del programma appena presentato.
delimitazione dei tag di tipo variable. La modifica
10.3 Jinja un template engine per dei delimitatori non è quindi locale al template ma
Python globale nell’ambiente.
Per proseguire nello studio della tecnologia dei Il template binario è ottenuto tramite il metodo
template con il linguaggio Python abbiamo scelto from_string() dell’ambiente mentre il testo finale
il modulo Jinja2 — versione 2.7.2 per Python3; viene generato chiamando il metodo render() con
la figura 7 ne mostra il logo — perché molto ricco la tupla dei nomi dei file da unificare:
di funzionalità; tra esse abbiamo i delimitatori #! /usr/bin/python3
adattabili per la generazione di sorgenti LATEX e # -*- coding: utf-8 -*-
la precompilazione dei template per incrementare
le prestazioni. from jinja2 import Environment

62
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Figura 6: L’editor idle in azione mostra una finestra principale con il codice dello script in Python e una finestra di shell
interattiva che raccoglie l’uscita dei programmi. Per eseguire il codice è sufficiente premere il tasto F5.

alla funzione successiva separata da un carattere |


che simboleggia un condotto. Questa funzionalità
è chiamata filtro, una scelta non particolarmente
felice perché essa è un’operazione di mappatura e
non di filtraggio.
Come esempio, nel prossimo script applicheremo
un filtro al titolo di sezione per renderlo in caratteri
maiuscoli e per arrotondare un numero decimale a
sei cifre significative:

from jinja2 import Environment


import math
Figura 7: Il logo del progetto Jinja rappresenta un tem-
pio giapponese per l’assonanza tra i termini ’template’ e env = Environment(
’temple’. variable_start_string="<<|",
variable_end_string="|>>"
)

env = Environment( tmpl = r"""


variable_start_string="<<|", \section{<<| sec_tit|upper |>>}
variable_end_string="|>>"
) A rounded version of the \( \pi \) value:
<<| "%0.6f"|format(pi) |>>.
tmpl = r""" """
\documentclass{minimal}
\usepackage{pdfpages} t_eng = env.from_string(tmpl)
\begin{document} print(t_eng.render(sec_tit="Important",
{% for doc in doc_list -%} pi = math.pi))
\includepdf[pages=-]{<<|doc|>>}
{% endfor -%}
\end{document}
Si tratta di dettagli di presentazione dei dati
""" perciò il template non viola troppo la regola fon-
damentale di separazione tra modello e vista — in
t_eng = env.from_string(tmpl) questo caso tra dati e testo finale — includendone
la logica. Le operazioni di elaborazione dovranno
docs = ("as541","as656","as163", riguardare esclusivamente proprietà di visualizza-
"as048","as525","as854") zione e mai la costruzione dei dati. In altre pa-
print(t_eng.render(doc_list=docs)) role, al template è deputata la responsabilità di
presentare i dati elaborandoli opportunamente.
10.5 Pipeline e filtri
Nell’esempio precedente il template non conosce
In Jinja esiste il concetto di pipeline di comandi: il valore di π e si occupa solamente di arrotondare
l’uscita di un comando verrà passata come ingresso un numero decimale.

63
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

Tabella 4: Una semplice tabella esaminata nel testo come


t_eng = env.from_string(tmpl)
vista sui dati. Il template si occupa dei dettagli come la
numerazione delle righe e il calcolo del totale di colonna. data = (
{'descr':"Site preparation",'val': 72030},
{'descr':"Excavation work", 'val': 242000},
N. Description Cost
{'descr':"Foundations", 'val':5078000},
1 Site preparation 72030.00 {'descr':"Main framework", 'val':8925000},
2 Excavation work 242000.00 )
3 Foundations 5078000.00 print(t_eng.render(data_list=data))
4 Main framework 8925000.00
Total 14317030.00 Nel listato 1 è riportato il codice esteso dello
script precedente in cui viene aggiunta una colon-
na i cui valori sono le percentuali date dei valori
10.6 Materiale tabellare corrispondenti di quella dei costi.
Consideriamo la tabella 4 come vista sui dati. Vor- La particolarità sta nel calcolo della somma di
remmo che il template si occupasse della formatta- queste percentuali a opera del template: nonostante
zione dei numeri a due posizioni decimali fisse, del la possibilità di utilizzare variabili tramite il tag
calcolo del totale di colonna e della numerazione set, non è possibile leggerne il valore modificato
automatica delle righe. all’interno del ciclo for perché gli incrementi riga
Tutto ciò è piuttosto semplice in Jinja grazie ad per riga rimangono locali al blocco.
alcune funzioni predefinite. Abbiamo già incontra- Abbiamo quindi utilizzato la lista vatlist, gesti-
to la funzione format() messa in pipeline con la ta in memoria come oggetto globale, appendendovi
stringa di formato. Un’altra utile funzione è sum(), i singoli valori all’interno di un blocco if vuoto.
che riceve una lista per sommarne gli elementi e All’esterno del ciclo che produce le righe della ta-
opzionalmente anche il nome dell’attributo asso- bella questa lista viene poi passata alla funzione
ciato al valore da considerare nel calcolo se il dato sum() che calcola il totale desiderato.
è, per esempio, un dizionario. Se nel modulo fosse previsto un tag global
Per la numerazione delle righe è sufficiente usare set, il codice ritornerebbe a essere quello che ci
l’oggetto loop che assieme a molte altre informazio- si aspetterebbe ma probabilmente le variabili glo-
ni contiene il contatore delle iterazioni nel campo bali invoglierebbero a inserire la logica dati nel
index. template.
Il manuale di riferimento online del template en-
10.7 Funzioni custom
gine presenta tutte le funzioni con esempi di codice
e non è difficile trovare proprio la funzione neces- In questo esempio vedremo come utilizzare nel
saria a risolvere un dato problema. Di seguito il template le funzioni personalizzate. Lo scopo è, lo
codice completo dello script che genera la tabella 4. ricordiamo ancora, modificare solo l’aspetto dei
dati per crearne una vista.
#! /usr/bin/python3
Lo script costruirà il codice LATEX della seguen-
# -*- coding: utf-8 -*-
te tabella composta da una prima colonna con i
from jinja2 import Environment numeri non formattati e da una seconda colon-
env = Environment() na con i numeri formattati con la nuova funzione
personalizzata.
tmpl = r"""
\begin{tabular}{clrr} No formatting Formatting
\toprule
N. & Description & Cost\\ 45613854.156 45 613 854,16
\midrule 546447.1 546 447,10
{% for w in data_list -%} 450215 450 215,00
{{ loop.index -}} 454 454,00
& {{ w.descr -}} 7899.3 7899,30
& {{ "%0.2f"|format(w.cost) }}\\
{% endfor -%} Per poter chiamare una funzione in un tag istru-
\midrule zione del template è sufficiente registrarla nel dizio-
& Total & {{ nario globals dell’oggetto Environment. La chia-
"%0.2f" ve dovrà coincidere con il nome della funzione
|format(
utilizzata internamente nel template.
data_list
|sum(attribute='val')) }}\\ Nello script si procede col definire una semplice
\bottomrule funzione di formato numerico a precisione fissa a
\end{tabular} due decimali, che inserisce un piccolo spazio tra i
""" gruppi di tre cifre se la parte intera del numero ne
ha almeno cinque, secondo le norme del Sistema

64
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Listato 1: Il codice dello script che genera una tabella compiendo calcoli percentuali e somme sui dati.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

from jinja2 import Environment

env = Environment()

tmpl = r"""
\begin{tabular}{clrr}
\toprule
N. & Description & Cost & \textsc{vat}\\
\midrule
{% set vatlist = [] -%}
{% for w in data_list -%}
{# just a trick...
#}{% if vatlist.append(w.cost*w.vat) -%}{%endif -%}
{{ loop.index -}}
& {{ "%-18s"|format(w.descr) -}}
& {{ "%10.2f "|format(w.cost) -}}
& {{ "%8.2f"|format(w.cost*w.vat) }}\\
{% endfor -%}
\midrule
& Total & {{
"%0.2f"
|format(data_list|sum(attribute='cost')) -}}
& {{ "%0.2f"|format(vatlist|sum) }}\\
\bottomrule
\end{tabular}
"""

t_eng = env.from_string(tmpl)
data = (
{'descr':"Site preparation", 'cost': 720, 'vat':.10},
{'descr':"Excavation work", 'cost': 2420, 'vat':.16},
{'descr':"Foundations", 'cost':50780, 'vat':.16},
{'descr':"Main framework", 'cost':89250, 'vat':.16},
)
print(t_eng.render(data_list=data))

# program's output:
'''

\begin{tabular}{clrr}
\toprule
N. & Description & Cost & \textsc{vat}\\
\midrule
1& Site preparation & 720.00 & 72.00\\
2& Excavation work & 2420.00 & 387.20\\
3& Foundations & 50780.00 & 8124.80\\
4& Main framework & 89250.00 & 14280.00\\
\midrule
& Total & 143170.00& 22864.00\\
\bottomrule
\end{tabular}
'''

65
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

Internazionale delle unità di misura.14 La funzione 10.9 Ereditarietà dei template


tiene conto anche di un parametro opzionale che L’ereditarietà è uno dei concetti più importanti
controlla il separatore decimale. Il codice completo della programmazione a oggetti. Consente di crea-
dello script è il seguente: re una classe contenente le funzionalità di base
comuni a una famiglia di oggetti sempre più spe-
#! /usr/bin/python3 cializzati. Nel caso dei modelli di documento un
# -*- coding: utf-8 -*- template di base può essere esteso da un nuovo
template indipendente.
from jinja2 import Environment
Se desideriamo produrre documenti diversi che
tuttavia abbiano in comune una serie di elementi,
def fmt_num(x, comma_sep=False):
if int(x) > 9999: non potremo riusare il codice con i template ricor-
res = "{:,.2f}".format(x) sivi perché l’inclusione dei moduli secondari sarà
res = res.replace(",","\\,") riferita a un template fisso e quindi a sempre e solo
else: un unico documento. La ricorsione esprime cioè la
res = "{:.2f}".format(x) struttura di un unico documento mentre l’eredita-
rietà rappresenta la struttura di una famiglia di
if comma_sep: documenti.
return res.replace(".", ",") Nel template base si troverà almeno un tag di
else: tipo istruzione di nome block che verrà poi, per
return res
così dire, implementato o esteso da un secondo
env = Environment()
template. Per esempio:
env.globals['fmt_num'] = fmt_num # base template:
t_m = r"""
tmpl = r""" \begin{document}
\begin{tabular}{rr} {% block content -%}
\toprule {% endblock -%}
No formatting & Formatting\\ \end{document}
\midrule """
{% for x in nums -%}
{{x}} & {{ fmt_num(x, True) }}\\ Il template che eredita un template base dichia-
{% endfor -%} rerà di estenderlo con il tag di nome extends e
\bottomrule poi definirà i blocchi base. Per esempio:
\end{tabular}
""" # child template
nums = (45613854.156, t_c = """\
546447.1, {% extends "main" -%}
450215, {% block content -%}
454, Great {{ thing }}!
7899.3) {% endblock -%}
t_eng = env.from_string(tmpl) """

print(t_eng.render(nums=nums)) L’ereditarietà in Jinja è una funzionalità molto


potente. Per esempio anche un singolo blocco può
essere esteso chiamando la funzione super() o può
10.8 Template ricorsivi
essere stampato più di una volta tramite la chiave
L’effetto è sostanzialmente simile a quello dei tem- self.<block-name>() oppure ancora può essere
plate figli di lustache esaminati alla sezione pre- inserito in un ciclo iterativo. Vediamo brevemente
cedente: in un template possono essere inclusi altri un esempio di ciascuna di queste funzionalità.
template secondari per mezzo del tag include. La La parola chiave super()
struttura che ne deriva è una gerarchia ad albero
dall’alto verso il basso che corrisponde a un unico La dimensione della pagina di una famiglia di do-
documento strutturato in parti e sottoparti. cumenti basati sulla classe article può essere fissata
tra le opzioni nel template di base; tali opzioni
Poiché i sorgenti LATEX sono composti da parti
potranno essere estese nel template derivato come
e sottoparti questa funzionalità è particolarmente
in questo codice:
utile. Un esempio di strutturazione dei template è
riportato nella sezione 11. # base template
t_m = r"""
14. Ulteriori informazioni possono essere trovate nella
\documentclass[{% block option -%}
documentazione del pacchetto siunitx oppure nella pagi- a4paper{% endblock %}]{article}
na web http://physics.nist.gov/cuu/Units/checklist. """
html al punto 16.

66
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

t_c = """\ 10.10 Un esempio di ereditarietà


{% extends "main" -%}
Memorizzando i template in file esterni avremo
{% block option -%}
{{ super() -}}
il vantaggio che le modifiche non interessanti un
{% if opt %},{% endif -%} cambiamento della struttura dei dati non riguar-
{{ opt|join(',') -}} deranno l’applicazione ma solamente questi file di
{% endblock -%} testo. Negli esempi di questa sezione i template
""" sono inclusi nel sorgente per scopi esplicativi e
continueremo a farlo anche se sarà necessario in-
Se la lista opt contiene le stringhe '12pt' e trodurre funzioni diverse da quelle che avremmo
'draft' il risultato sarà: utilizzato in un’applicazione reale.
\documentclass[a4paper,12pt,draft]{article} In Jinja la sequenza di passi effettiva infat-
ti è quella di utilizzare un apposito loader per
La parola chiave self.<block-name>()
leggere i template da disco — anche in forma
Con lo stesso esempio dimostriamo la capacità d’in- precompilata. Questo è il motivo per cui il meto-
serire copie del testo di un blocco con la funzione do from_string(), usato fino a ora negli esempi
self.<block-name>() riportata nel template di per generare oggetti Template, non prevede che i
base: template possano rappresentare una gerarchia.
# base template Tuttavia non è obbligatorio che i template si tro-
t_m = r""" vino memorizzati nel file system perché il modulo
\documentclass[{% block option -%} Jinja mette a disposizione l’oggetto DictLoader
a4paper{% endblock %}]{article} che elabora un dizionario e restituisce il loader con
% le opzioni di classe sono: cui caricare le stringhe del template base e del
% {{ self.option() }} template d’estensione con i nomi associati.
""" C’è da notare che il metodo get_template()
e il risultato sarà: dovrà richiedere all’oggetto Environment il tem-
plate figlio e non quello base perché è il primo che
\documentclass[a4paper,12pt,draft]{article} ne eredita ed estende il contenuto.
% le opzioni di classe sono: Nell’esempio presentato il risultato atteso dei
% a4paper,12pt,draft template sono i due sorgenti LATEX che hanno lo
Blocchi innestati e la parola chiave scoped stesso preambolo ma un diverso corpo:
Nel template di base un blocco è incluso all’interno \documentclass{article}
di un ciclo iterativo, per esempio perché contiene \usepackage[T1]{fontenc}
il testo di una riga di una tabella composta con \usepackage[utf8]{inputenc}
l’ambiente tabular. Perché le variabili di ciclo siano \usepackage[italian]{babel}
visibili all’interno del blocco è necessario specificare \begin{document}
Great job!
la chiave scoped per modificare il comportamento
\end{document}
difensivo del template:
t_m = r""" \documentclass{article}
\begin{tabular}{cc} \usepackage[T1]{fontenc}
{% for key, value in mydict.items() -%} \usepackage[utf8]{inputenc}
{% block row scoped -%}{% endblock -%} \usepackage[italian]{babel}
{% endfor -%} \begin{document}
\end{tabular} Hello Albert!
""" \end{document}

t_c = """\ Il codice completo dello script che li realizza è il


{% extends "main" -%} seguente:
{% block row -%}
#! /usr/bin/python3
{{key}} & {{value}}\\\\
# -*- coding: utf-8 -*-
{% endblock -%}
"""
from jinja2 import Environment, DictLoader
Se il dizionario contiene le seguenti infor-
mazioni {'font':'12pt', 'paper':'a4paper', t_m = r"""
\documentclass{article}
'version':'draft' il risultato sarà:
\usepackage[T1]{fontenc}
\begin{tabular}{cc} \usepackage[utf8]{inputenc}
paper & a4paper\\ \usepackage[italian]{babel}
version & draft\\ \begin{document}
font & 12pt\\ {% block content -%}
\end{tabular} {% endblock -%}

67
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

Tabella 5: Tabella con dati di esempio rappresentativa


\end{document}
dell’aspetto del report d’inventario secondo le specifiche
""" definite nel testo.

t_c1 = """\ Supplier: Good Shirts


{% extends "main" -%} Description Q.ty Cost ($) Total ($)
{% block content -%} AQ12 PL21 — T-Shirt Pluton series 12 12.50 150.00
Great {{ thing }}! SZ02 PL24 — T-Shirt Pluton series 8 19.10 152.80
W964 JP02 — T-Shirt Jupiter series 6 12.90 77.40
{% endblock -%} BP01 JP10 — T-Shirt Jupiter series 20 15.80 316.00
""" XVYY E5 — T-Shirt Earth series 5 14.80 74.00
XVYY E19 — T-Shirt Earth series 3 12.20 36.60
XVYY E41 — T-Shirt Earth series 19 11.30 214.70
t_c2 = """\ NEAR 956 — T-Shirt Moon series 5 14.80 74.00
{% extends "main" -%} 78 1 095.50
{% block content -%}
Hello {{ name }}!
{% endblock -%}
"""
(c) chiusura della connessione.
2. costruzione del sorgente LATEX:
loader = DictLoader(
{'main':t_m, (a) definizione delle funzioni accessorie per
'content1':t_c1, la presentazione dei dati;
'content2':t_c2}
) (b) caricamento del template;
env = Environment(loader=loader) (c) unione dei dati;
(d) memorizzazione del risultato su file.
tmpl1 = env.get_template('content1')
print(tmpl1.render(thing="job")) Come sarà ormai chiaro, scriveremo il program-
ma in Python3, che fornisce con i propri moduli di
tmpl2 = env.get_template('content2')
libreria l’accesso immediato a database SQLite3,
print(tmpl2.render(name='Albert'))
e produrremo il sorgente con il template engine
Jinja. Questa procedura è più efficiente e affidabile
11 Secondo caso d’uso: l’inventario che non quella basata sull’utilizzo di fogli di calcolo
In questo esempio reale mostreremo come produrre perché l’uso di un database assicura la coerenza
un report d’inventario di un esercizio commerciale e l’integrità dei dati e al contempo l’evoluzione
interrogando un database relazionale. I dati sono futura della struttura relazionale.
memorizzati in un database SQLite3 (SQLite3). Per dare un’idea delle prestazioni del proces-
Libreria libera estremamente diffusa e collaudata, so descritto, il tempo di esecuzione per creare il
progettata per l’utilizzo efficiente su postazioni lo- sorgente di un inventario reale di circa 8000 arti-
cali, SQLite3 non si basa sulla classica architettura coli è risultato essere di 0,17 s sulla macchina di
client/server distribuita su una rete e non c’è quin- riferimento.
di la necessità di effettuare alcuna configurazione 11.1 Specifiche del report
d’accesso mentre è immediata la gestione dei dati
salvati in un singolo, ordinario file locale. Si trat- L’inventario annuale è l’elenco della merce presente
ta comunque di un database engine transazionale in magazzino alla data del 31 dicembre. Questo
acid15 compliant, multipiattaforma e disponibile elenco dovrà essere suddiviso per reparto e per
praticamente per qualsiasi sistema operativo nella fornitore riportando i totali parziali sia di quantità
forma di un file sorgente scritto in linguaggio C che di costo. Al termine dell’elenco dovrà esse-
per l’inclusione diretta in applicazioni o in librerie re presentata una tabella riassuntiva per reparto
a caricamento dinamico. per dare le proporzioni generali di consistenza del
magazzino.
Il programma di creazione del report sarà sud-
Nell’elenco ogni riga sarà formata dalle seguenti
diviso in due principali fasi d’esecuzione: il repe-
informazioni: un breve testo descrittivo che defini-
rimento delle informazioni dal database e la con-
sce univocamente l’articolo, la quantità inventaria-
seguente produzione del sorgente LATEX a partire
ta, il costo del singolo pezzo e il totale risultante.
dalle informazioni ottenute; in dettaglio:
L’elenco dovrà apparire su due colonne per pagina
1. consultazione del database sql: e dovrà essere presente un frontespizio.
Nella tabella 5 è riportato un breve possibile
(a) connessione al database; estratto dell’elenco d’inventario composto secondo
le specifiche decise per il documento finale.
(b) esecuzione delle query e inserimento dati
in memoria; 11.2 Gerarchia dei template
15. Per maggiori informazioni si consulti la pagina web Per produrre il report dell’inventario, anziché uti-
https://en.wikipedia.org/wiki/ACID. lizzare un unico template ne introdurremo uno

68
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

principale che definirà il preambolo del sorgente \rule[.5ex]{180pt}{1pt}


e il frontespizio e altri due sub-template, uno per \end{document}
inserire l’elenco degli articoli e l’altro per comporre
la tabella riassuntiva finale. In questo modo è più 11.4 Il sub-template elenco
semplice gestire e mettere a punto le singole parti Nel seguente codice è riportato l’intero contenuto
rispettando la struttura effettiva del documento del sub-template che formerà l’elenco degli articoli
finale. I template saranno salvati su file e potranno secondo le specifiche del report. Tutto il testo è
essere modificati indipendentemente dal codice del- racchiuso in un ciclo eseguito per ogni reparto,
lo script. Faremo uso di funzionalità della libreria il che significa che verranno prodotti altrettanti
Jinja non ancora presentate negli esempi già visti; ambienti supertabular.
questi ultimi includono i template come semplici La funzione Python items(), definita per il ti-
variabili stringa definite nello script. po dizionario, restituisce un iteratore a due valo-
ri: il primo è la chiave e il secondo è il valore a
11.3 Il template principale essa associato. Corrispondentemente lo script de-
finirà il campo data come un dizionario {hnome
Useremo la classe LATEX report con i pacchetti
repartoi/hdizionario fornitorii}.
di base per impostare codifiche, dimensione della
Per ogni reparto definiamo la prima riga della
pagina, gabbia del testo, testatine, font, numero di
tabella dell’elenco e l’ambiente supertabular con-
colonne; useremo poi il pacchetto supertabular per
tenente un ulteriore ciclo annidato per ciascun
comporre l’elenco degli articoli inventariati sotto
fornitore sulle variabili {hcodice fornitorei/hlista
forma di tabella a più pagine per ciascun reparto.
articolii}.
Per brevità riporteremo solamente il corpo del
Per ogni fornitore il ciclo di ultimo livello inseri-
sorgente LATEX inserito nel template principale.
rà le righe della tabella con descrizione, quantità,
Dopo aver definito le righe d’intestazione e di coda
costo e costo totale, a rappresentare la struttura ri-
previste dal pacchetto supertabular, troviamo le due
portata schematicamente in figura 8. Tutti i campi
istruzioni per l’inclusione dei sub-template tramite
sono formattati per essere lunghi lo stesso numero
il nome del corrispondente file racchiuso tra doppi
di caratteri in modo che nel sorgente i dati risulti-
apici:
no allineati in verticale — in particolare i valori di
costo sono arrotondati a due cifre decimali — per
\begin{document}
mezzo delle funzioni format() e truncate().
\maketitle
\twocolumn
Il template prevede anche che sia definita una
funzione di nome get_sup_name() la quale, dato
\scriptsize il codice, restituisca il nome completo del fornitore
\sffamily e che i valori delle somme parziali di quantità e
costi siano contenute in un dizionario strutturato
% intestazione tabella d'inizio pagina per livelli hrepartoi/hfornitorei.
\tablehead{
\hline <<% for dep_name, sup_list in
Descrizione articolo & Q.tà & Costo \euro & data.items()-%>>
Esist. \euro\\\hline} \tablefirsthead{
\hline
\tabletail{% coda a fine pagina tabella \multicolumn{4}{c}{%
\hline --- REPARTO <<| dep_name|upper |>> ---}\\
\multicolumn{4}{r}{\textit{segue\dots}}\\ \hline
} }

% ultima riga tabella \begin{supertabular}{lrrr}


\tablelasttail{\hline \\\hline
\multicolumn{4}{r}{% Descrizione articolo & Q.tà & Costo \euro
\textit{Fine tabella di reparto.}}\\} & Esist. \euro\\\hline
<<% for sup_code, artlist
<<% include "supertab-tmpl.tex" -%>> in sup_list.items() -%>>
\\
\newpage \multicolumn{4}{l}{%
\onecolumn Fornitore <<|get_sup_name(sup_code) |>>}\\
\normalsize \hline
<<% for art in artlist -%>>
<<% include "finaltable-tmpl.tex" -%>> <<| "%-38s"|format(art.descr)
|truncate(38, end="")
\bigskip |>> & <<| "%5d"|format(art.qty)
\rule[.5ex]{180pt}{1pt} |>> & <<| "%6.2f"|format(art.cost)
Fine documento |>> & <<| "%7.2f"|format(art.qty*art.cost)

69
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

|>> \\ "%0.2f"|format(sums.values()
<<% endfor -%>> |sum(attribute='tottot'))
\hline |>>}\\\hline
<<| "%-38s"|format("Tot.") \end{tabular}
|>> & <<| "%5d"|format(sums[dep_name] \end{center}
['suptot']
[sup_code] Osservando il codice dei sub-template si nota
['qty']) che alcune operazioni di calcolo sono effettuate dal
|>> & & <<| "%8.2f"|format(sums[dep_name] programma mentre altre dal template. Il compito
['suptot'] di trovare il migliore equilibrio tra un template
[sup_code] completamente privo di logica e uno che effettua
['tot']) tutte le elaborazioni sull’insieme minimo dei dati è
|>> \\ lasciato al lettore come ulteriore esercizio. L’equi-
\hline librio intermedio dei template qui proposti è stato
<<% endfor -%>>
guidato da semplici considerazioni di efficienza nei
\\
calcoli dimostrando forse che le due situazioni estre-
\\
\hline me peccano la prima di complessità del codice e la
<<| "Tot. Rep. %-32s"|format(dep_name) seconda di efficienza del template.
|>> & <<| "%5d"|format(sums[dep_name] Proprio le ultime righe del sub-template per
['qtytot']) generare il quadro riassuntivo provano come le
|>> & & <<| "%8.2f"|format(sums[dep_name] somme parziali per fornitore e reparto sono lasciate
['tottot']) allo script mentre i totali d’inventario sono lasciati
|>> \\ al template attraverso la funzione filtro sum() che
\hline opera sulla lista dei valori del dizionario ottenuta
\end{supertabular} con la funzione values().
<<% endfor -%>>
11.6 Il database
11.5 Il sub-template quadro Ipotizziamo che il database esponga una vista che
Per costruire il quadro riassuntivo utilizziamo un semplifichi la query degli articoli di magazzino.
ambiente tabular centrato nella pagina. Poiché le Una vista appare come una normale tabella di sola
cifre saranno elevate, per aumentarne la leggibilità lettura del database mentre invece è ricavata al
faremo uso della macro \num del pacchetto siunitx. momento da una query prestabilita salvata perma-
Il seguente codice è l’intero contenuto del sub- nentemente nella base di dati. A noi permetterà di
template. Occorre un ciclo sulla variabile sums per non entrare nei dettagli della struttura dei dati, pro-
produrre tutte le righe della rilevazione complessiva babilmente articolata in ordini, fornitori, reparti,
dei singoli reparti. stagioni, articoli, rilevazioni d’inventario, eccetera.
Del resto, la vista serve proprio a questo: costituire
\begin{center} l’interfaccia ad alto livello ai dati nascondendo-
\begin{tabular}{lcr} ne i dettagli. Se cambiasse la struttura interna la
\hline vista continuerebbe a essere la stessa una volta
\multicolumn{3}{c}{% modificata adeguatamente la query sottostante.
Inventario al 31/12/<<| year |>>}\\
\hline 11.7 Lo script
\\
L’iterazione sulle tuple restituite dalla query crea il
\multicolumn{3}{l}{%
dizionario che memorizza l’intero insieme dei dati
Tabella riassuntiva per reparto}\\[2ex]
\hline d’inventario chiamato inv_data. La sua struttura
Nome reparto &Esistenza &Valore Esistenza è rappresentata schematicamente nella figura 8 e
\euro\\\hline corrisponde esattamente a quella attesa dal tem-
<<% for dep_name, dep_data plate. Completata la fase di reperimento delle in-
in sums.items() -%>> formazioni dal database la funzione calc_sums()
Reparto <<| "%-18s"|format(dep_name) visita la struttura dati d’inventario per generare
|>> & \num{<<| un secondo dizionario contenente i subtotali per
"%10d"|format(dep_data.qtytot) fornitore e reparto.
|>>} & \num{<<| Il codice del template principale è contenu-
"%0.2f"|format(dep_data.tottot)
to nel file inventory.tex nella sottocartella
|>>}\\
<<% endfor -%>>
templates. Daremo quest’informazione all’ogget-
\hline to Environment (tramite il loader di Jinja) in
Totale inventario & \num{<<| aggiunta alle stringhe utilizzate come delimitato-
"%10d"|format(sums.values() ri per le istruzioni e i campi espressione. Chia-
|sum(attribute='qtytot')) mando poi il template principale tramite il meto-
|>>} & \num{<<| do get_template(), implicitamente verranno an-

70
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

{‘dep-key’: {‘sup-key’:[ {‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},


{‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},
{‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},
{‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},
{‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},
{‘descr’:<val>, ‘qty’:<val>, ‘cost’:<val>},
...

...
]

...
}
}
Figura 8: Schema della struttura a più livelli in cui sono memorizzati tutti gli articoli d’inventario. I simboli sono gli
stessi dei tipi in Python ovvero le parentesi graffe definiscono un dizionario e le parentesi quadre una lista. I tre puntini
indicano la ripetibilità dell’elemento dello stesso livello. Nel template questo dizionario è facilmente iterato su tre livelli di
annidamento per generare il codice LATEX di ambienti tabular multi-pagina dell’elenco degli articoli in inventario. Una
struttura analoga è utilizzata nel codice applicativo per memorizzare i valori dei subtotali per fornitore e reparto. L’utente
può disegnare schemi simili per facilitarsi la scrittura del codice.

che caricati i sub-template posizionati nella stessa inv_data[dep][sup] = []


directory. inv_data[dep][sup].append(
Il codice si conclude chiamando il metodo {"descr":item[1],
render() dell’oggetto template e salvando il risul- "qty":item[2],
tato testuale su disco pronto per essere compilato "cost":item[3]})
dal motore di composizione:
conn.close()
#! /usr/bin/python3.4
# -*- coding: utf-8 -*- # utility template functions

from jinja2 import Environment # return supplier long name by code


from jinja2 import FileSystemLoader def get_sup_name(sup):
import sqlite3 return sups[sup]

year = 2014 # a function to calculate the sums


def calc_sums(data):
# database section res = dict()
conn = sqlite3.connect("../db/archive.sqlite") for dep_name, sup_dict in data.items():
if not dep_name in res:
# a dictionary for suppliers info res[dep_name] = {'suptot':{}}
sups = dict() # key code => long name qtytot = 0
for row in conn.execute("SELECT * FROM suppliers;"): tottot = 0.0
sups[row[0]] = row[1]
for sup_code, item_list
# a dictionary with all warehouse items in sup_dict.items():
inv_data = dict() if not sup_code in res[dep_name]:
res[dep_name][sup_code] = {}
# the 'inv' table is a view qty = 0.0
# data are ordered by code tot = 0.0
query = """ for a in item_list:
SELECT code, descr, qty, cost, sup, dep qty += a['qty']
FROM inv tot += a['qty']*a['cost']
ORDER BY code; res[dep_name]
""" ['suptot']
for item in conn.execute(query): [sup_code] = {"qty":qty,
dep = item[5] "tot":tot}
if not dep in inv_data: qtytot += qty
inv_data[dep] = dict() tottot += tot
sup = item[4]
res[dep_name]['qtytot'] = qtytot
if not sup in inv_data[dep]: res[dep_name]['tottot'] = tottot

71
Roberto Giacomelli, Gianluca Pignalberi ArsTEXnica Nº 20, Ottobre 2015

return res Impostare l’intero processo in LuaTEX è un me-


todo ancora tutto da valutare ma potenzialmente
# template section interessante perché la connessione con TEX può
env = Environment( essere molto più stretta di quanto non accada con
loader=FileSystemLoader('./templates'), la produzione separata dei sorgenti. Si spera che in
variable_start_string="<<|",
futuro anche questo metodo si aggiunga pienamen-
variable_end_string="|>>",
block_start_string="<<%",
te alle soluzioni già disponibili e qui sinteticamente
block_end_string="%>>", esposte.
)
13 Ringraziamenti
tmpl = env.get_template("inventory.tex")
res = tmpl.render({ Ringraziamo l’amico Francesco Endrici, membro
"year":year, attivo della comunità del guIt, per averci fornito
"data":inv_data, un problema reale molto articolato di produzione
"sums":calc_sums(inv_data) di un documento a partire da un insieme di dati,
}) perfetto per mostrare l’uso delle tabelle di Lua per
descrivere informazioni strutturate.
# saving the content in a TeX file Ringraziamo infine la Redazione di ArsTEXnica
f = open("inv-{0:d}.tex".format(year), "w") per tutto il prezioso lavoro fatto su questo articolo,
f.write(res) dalla valutazione e revisione della bozza fino alla
f.close()
compilazione finale.

12 Conclusioni Riferimenti bibliografici


I problemi di elaborazione dati per la produzione
ABS guide (2015). «Advanced bash-scripting gui-
di documentazione sono molto frequenti in ogni
de». Web site http://www.tldp.org/LDP/abs/
ambito e non è difficile acquisire le conoscenze per
html/index.html.
risolverli sviluppando script o programmi che pro-
ducano in uscita i sorgenti per la composizione TEX. Battistin, R. (2014). «LATEX per la stesura
Il vantaggio principale è che, tenendo separata la dei rapporti di prova». ArsTEXnica, (18), pp.
parte di elaborazione dati dalla parte della com- 55–63. URL http://www.guitex.org/home/
posizione tipografica, possiamo far evolvere e/o numero-18.
correggere singolarmente le due parti senza per
questo dover stravolgere l’intero processo. L’elabo- De Marco, A. e Cresci, P. E. (2011). «LATEX
razione dei dati sarà influenzata dalla, e influenzerà nella pubblica amministrazione. La web appli-
la, scelta del linguaggio di programmazione in base cation FACILE per la produzione automatica
alle strutture dati fornite e alle librerie disponibili, di comunicazioni interne del Comune di Napo-
specialmente per il templating. li». ArsTEXnica, (12), pp. 39–56. URL http:
Nell’articolo sono stati presentati in dettaglio //www.guitex.org/home/it/numero-12.
alcuni esempi di programmi scritti in bash, C, Lua
Downey, A. B. (2012). Think Python: How To
e Python, privilegiando gli ultimi due linguaggi in
Think Like a Computer Scientist. O’Reilly Media.
ragione della loro diffusione e della loro semplicità
URL http://faculty.stedwards.edu/mikek/
rispetto al C. Si spera che la lettura di questo arti-
python/thinkpython.pdf.
colo aiuti l’utente a valutare se e come modificare
l’approccio ai propri problemi di produzione della Fiandrino, C. (2014). «MenùTEX: una piattafor-
documentazione ogni volta che i dati abbiano un ma per realizzare menù». ArsTEXnica, (18), pp.
grado di complessità significativo. 145–151. URL http://www.guitex.org/home/
L’ostacolo forse più grande per l’utente TEX numero-18.
all’approccio della generazione automatica di sor-
genti è acquisire i concetti di base del linguaggio GNU Bash (2015). «Bourne-again shell ma-
di programmazione scelto. Esiste però almeno un nual». http://www.gnu.org/software/bash/
metodo quasi alternativo: utilizzare LuaTEX per manual/.
elaborare i dati direttamente nel motore tipografi-
co per poi comporre il documento con le tecnologie Ierusalimschy, R. (2013). Programming in Lua,
di interazione Lua-TEX. Nemmeno così l’utente è Third Edition. Lua.Org.
esentato dall’imparare un linguaggio di program- Jinja (2015). «Jinja is a full featured template
mazione — in questo caso obbligatoriamente Lua — engine for python». Official web site http://
per scrivere le funzioni di accesso ai database o jinja.pocoo.org/.
tradurre i dati dai fogli elettronici, tuttavia nuove
librerie di reporting in Lua per LuaTEX potrebbero Kernighan, B. W. e Ritchie, D. M. (2004). Il
facilitare molto il compito di sviluppo. linguaggio C. Pearson.

72
ArsTEXnica Nº 20, Ottobre 2015 Generare documenti LATEX

Klemens, B. (2014). 21st century C. O’Reilly and SQLite3 (2015). «The sqlite consortium». Web
Associates. site https://www.sqlite.org/.
Mustache (2015). «{{mustache}} logic-less tem- Tantau, T. (2015). «pgf manual version 3.0.1».
plates». Official web site https://mustache. Available on www.ctan.org.
github.io/.
Olivine-Labs (2015). «lustache - logic-less {{mu-
stache}} templates with lua». Official web site . Roberto Giacomelli
http://olivinelabs.com/lustache/. giaconet dot mailbox at gmail dot com
. Gianluca Pignalberi
Pilgrim, M. (2009). Dive Into Python
g dot pignalberi at alice dot it
3. Apress. URL http://getpython3.com/
diveintopython3/.

73

Potrebbero piacerti anche