Robitex's Blog

Ideas in the web

Archivi Categorie: luatex

GuIT Channel


L’associazione GuIT ha aperto il canale Telegram GuIT Channel.

Ogni giorno vengono pubblicate notizie sul mondo TeX e LaTeX, suggerimenti, segnalazioni di nuovi pacchetti, curiosità e molto altro.

Per iscriversi fate click sul seguente link: https://t.me/guitchannel, oppure consultate il canale cercandolo con la stringa “guit channel” all’interno di una delle applicazioni di Telegram.

Iscrivetevi e diffondete la notizia!

Grazie ūüėČ

 

 

Annunci

Un simpatico effetto funzionale in LuaTeX


La domanda

A volte capita di chiedersi all’improvviso se l’idea che ci √® appena balenata in mente pu√≤ effettivamente esser realizzata.

Stavo esaminando alcune caratteristiche di LuaTeX quando una domanda √® sorta spontanea: √® possibile creare una macro che accetti una funzione matematica scritta dall’utente e la esegua su un argomento anch’esso variabile?

Si tratta quindi di far eseguire una funzione matematica definita sul momento, in pieno stile functional.

La macro dovrebbe accettare due argomenti: il primo √® la funzione da usare e il secondo l’argomento numerico su cui eseguire il calcolo:

\applyfun{<funzione>}{<numero>}

Nel documento troveremo poi il risultato cercato. La cosa interessante è che il primo parametro della macro, la funzione, è concettualmente simile al secondo argomento essendo entrambi dei valori.

Per esempio, vorremmo poter scrivere:

Il quadrato di 10 è \applyfun{function (x) return x*x end}{10}!

LuaTeX della famiglia TeX

LuaTeX √® un motore di composizione tipografico della gloriosa famiglia TeX. Questi programmi compilano un file di testo scritto dall’utente per fornire il corrispondente documento pdf.

Nel file di testo — detto sorgente — si adopera una sorta di linguaggio istruendo il programma alla gestione di sommari, riferimenti incrociati, titoli, eccetera.

In molti paesi opera un TUG, un TeX User Group. In Italia è attivo da 15 anni il GuIT Gruppo di utilizzatori Italiani di TeX, il cui sito può darvi tutte le informazioni necessarie.

Dimenticavo di dirvi che il sistema TeX √® stato uno dei primi esempi di software libero, perci√≤ siete ancor pi√Ļ liberi di usarlo per scrivere i vostri documenti.

La risposta

Ebbene la risposta è positiva! Si, è possibile e se non ci credete compilate con LuaTeX il codice seguente:

% luatex

% definizione della macro
\def\applyfun#1#2{%
\directlua{
local fun = #1
tex.sprint(fun(#2))
}}

Experimental!

\applyfun{function (x) return math.log(x)+10 end}{12}

Ok.

Il quadrato di $\pi$ vale
\applyfun{function (x) return x*x end}{3.14159}.

End job.
\bye

E mi raccomando, non provate a farlo con Word o LibreOffice…
Alla prossima.
R.

Napoli: tutto pronto per il GuITmeeting


GuITmeeting2012

Tra pochissimi giorni, Sabato 27 ottobre 2012 si terr√† a Napoli il GuITmeeting 2012, uno dei pi√Ļ importanti tra i meeting organizzati dal GuIT, Gruppo utilizzatori italiani di TeX, che per la prima volta non si svolger√† nell’aula magna della scuola Sant’Anna di Pisa, ma nell’altrettanto prestigiosa sala del Centro congressi Partenope dell’Universit√† degli Studi di Napoli Federico II.

Anch’io parteciper√≤ molto volentieri essendo perfino riuscito a presentare un lavoro di cui vi posso dire solo il titolo: “La grafica ad oggetti con LuaTeX”.

Non mancate sarà una bellissima occasione

Qualsiasi cosa stiate facendo, collegatevi subito con il sito del GuIT alla pagina del modulo d’iscrizione ūüôā !
Grazie, ci vediamo a Napoli!

LuaTeX and METAPOST


La libreria MPlib in LuaTeX

METAPOST, il noto linguaggio di disegno vettoriale, è stato incluso in LuaTeX sotto forma della libreria MPlib. Possiamo quindi inserire codice METAPOST direttamente in un sorgente LuaTeX.

Questa modalit√†, non richiede conversioni del formato d’uscita di METAPOST (.mps) ne la necessit√† di particolari opzioni di compilazione, mentre per procedere ancora pi√Ļ velocemente con l’unico passo di compilazione, basta utilizzare un editor come TeXWorks, in cui LuaTeX si avvia con un semplice click.
Da riga di comando darete invece la classica istruzione:

... alla riga di comando
... (occorre una installazione di
... TeX Live abbastanza recente)

$ luatex nomefile

Sorgente modello

In concreto, trovate di seguito il template del codice in linguaggio TeX dove si dovrà inserire il codice METAPOST tra le macro \mplibcode e \endmplibcode, con le uniche attenzioni di non lasciare in esso, ne righe vuote ne commenti con il carattere percentuale:

% source template
% LuaTeX with Metapost

\input luamplib.sty
\nopagenumbers

\mplibcode
   <codice METAPOST>
\endmplibcode
\bye

Il codice fornir√† il pdf della figura che scontorneremo poi con l’utility pdfcrop (ecco perch√© si sopprime la numerazione di pagina), per inserire il risultato in altri documenti. Naturalmente, mutatis mutandis, potremo lavorare allo stesso modo ma con LuaLaTeX per avere a disposizione la potenza espressiva di LaTeX.

Un esempio è riportato nel seguente codice, che produce la figura successiva scaricabile come pdf.

% a drawing example

\input luamplib.sty
\nopagenumbers

\mplibcode
beginfig(1);
u := 100mm;
for a = 0 step 15 until 90:
draw origin -- u*dir(a)
  withpen pencircle scaled 2pt
  withcolor red;
endfor;
endfig;
\endmplibcode
\bye

An example of a METAPOST figure building directly in LuaTeX engine.


Non male!
Un saluto.
R.

Numeri in lettere


Scarica l’articolo in formato pdf per la stampa

Un problema antico: tradurre numeri in lettere

Molti anni fa acquistai un libro che proponeva una serie di esempi, di esercizi, tutti risolti con il linguaggio Basic.
Nonostante il fatto che quel libro una volta prestato non venne pi√Ļ restituito (ebbene l’interessato/a ovunque esso/a sia, √® ancora in tempo per restituire quel libro e, gi√† che c’√©, anche gli altri libri di storia dell’arte, grazie), ricordo che verso la fine esso presentava il problema di tradurre un numero in lettere, per esempio, se l’input fosse stato 123 allora l’output del programma avrebbe dovuto essere “centoventitre”.

Soluzione in Java

Malauguratamente il linguaggio Java non √® in grado di far “ritrovare” i libri “perduti” ai legittimi lettori (anche se credo che Oracle stia lavorando a funzionalit√† del genere), ma √® comunque in grado di dare soddisfazione a chi volesse tradurre vaghi e primordiali ricordi, in una pagina web di un pugno di bit.

Cominciamo allora con qualcosa di molto pi√Ļ sofisticato di quello che si trovava nel codice Basic di quel libro notando che il numero 123 si pu√≤ tradurre in lettere in tempi successivi scrivendo prima “cento” e continuando con il numero rimanente 23, scrivendo poi “venti” e continuando ancora con il numero 3, traducendolo in “tre”. Se pensiamo di unire le tre parole avremo ottenuto “centoventitre”.

Se il numero da tradurre in parole fosse invece 6123, dapprima scriveremmo “seimila” e poi ritrovando ancora il numero 123 applicherei la stessa procedura precedente ed alla fine otterrei la sequenza “seimilacentoventitre”.

Adotteremo quindi un approccio ricorsivo per cui elaborata la cifra pi√Ļ significativa si lancia la procedura stessa con il numero restante. Il codice che compare di seguito evita i casi di doppia vocale. Applicando strettamente la procedura ricorsiva, il numero 41 diventerebbe “quarantauno” invece di “quarantuno”. Inoltre, grazie all’overloading dei metodi, il programma √® in grado di elaborare numeri decimali traducendoli nel formato letterale previsto per gli importi in euro.

import java.util.Scanner;

public class Traduci {
    // aggiunto il metodo in overloading che fa
    // corrispondere una cifra in euro alla stringa convenzionale
    // es.: 58,45 -> "cinquantotto/45"

    public static void main(String[] args) {
        System.out.print("Digita il numero da tradurre: ");
        Scanner in = new Scanner(System.in);
        if (in.hasNextInt()) {
            System.out.println("in lettere: " +
                               Traduci.NumberToText(in.nextInt()));
        } else if (in.hasNextDouble()) {
            System.out.println("in lettere: " +
                               Traduci.NumberToText(in.nextDouble()));
        } else {
            System.out.println("Errore nei dati immessi.");
        }
    }

    static String NumberToText(int n) {
    // metodo wrapper
        if (n == 0) {
            return "zero";
        } else {
            return NumberToTextRicorsiva(n);
        }
    }
    
    static String NumberToText(double importo) {
        // metodo wrapper
        int n = (int) Math.round(importo * 100);
        int parteIntera = n/100;
        String parteDecimale = String.format("%02d", n%100);

        if (parteIntera == 0) {
            return "zero/" + parteDecimale;
        } else {
            return NumberToTextRicorsiva(parteIntera) + "/" + parteDecimale;
        }
    }

    private static String NumberToTextRicorsiva(int n) {
        if (n < 0) {
            return "meno " + NumberToTextRicorsiva(-n);
        } else if (n == 0){
            return "";
        } else if (n <= 19){
            return new String[] { "uno", "due", "tre", "quattro", "cinque", 
                                  "sei", "sette", "otto", "nove", "dieci", 
                                  "undici", "dodici", "tredici", 
                                  "quattordici", "quindici", "sedici", 
                                  "diciassette", "diciotto", "diciannove" }[n-1];
        } else if (n <= 99) {
            String[] vettore = 
            { "venti", "trenta", "quaranta", "cinquanta", "sessanta", 
              "settanta", "ottanta", "novanta" };
            String letter = vettore[n / 10 - 2];
            int t = n % 10; // t è la prima cifra di n
            // se è 1 o 8 va tolta la vocale finale di letter
            if (t == 1 || t == 8 ) {
                letter = letter.substring(0, letter.length() - 1);
            }
            return letter + NumberToTextRicorsiva(n % 10);
        } else if (n <= 199){
            return "cento" + NumberToTextRicorsiva(n % 100);
        } else if (n <= 999){
            int m = n % 100;
            m /= 10; // divisione intera per 10 della variabile
            String letter = "cent";
            if (m != 8){
              letter = letter + "o";
            }
            return NumberToTextRicorsiva(n / 100) + letter + 
                NumberToTextRicorsiva(n % 100);
        }
        else if (n <= 1999){
            return "mille" + NumberToTextRicorsiva(n % 1000);
        }  else if (n <= 999999){
            return NumberToTextRicorsiva(n / 1000) + "mila" + 
                NumberToTextRicorsiva(n % 1000);
        }
        else if (n <= 1999999){
            return "unmilione" + NumberToTextRicorsiva(n % 1000000);
        } else if (n <= 999999999){
            return NumberToTextRicorsiva(n / 1000000) + "milioni" + 
                NumberToTextRicorsiva(n % 1000000);
        } else if (n <= 1999999999){
            return "unmiliardo" + NumberToTextRicorsiva(n % 1000000000);
        } else {
            return NumberToTextRicorsiva(n / 1000000000) + "miliardi" + 
                NumberToTextRicorsiva(n % 1000000000);
        }
    }
}

Una volta inserito il codice in un file di testo chiamato “Traduci.java”, procedete alla sua compilazione utilizzando uno JDK 5.0 o successivo con il comando (installate eventualmente da voi un JDK scelto tra le varie alternative):

javac Traduci.java

Per lanciare il programma che consiste nel file contenente il bytecode per la macchina virtuale Java ed ha per estensione .class, date il comando:

java Traduci

ed inserite il numero intero o decimale da tradurre in parole. Nell’immagine seguente potete consultare la sessione di compilazione ed esecuzione.

The compile session of the java program

The compile session of the java program

Soluzione in Lua

Vogliamo adesso scrivere lo stesso programma in Lua, non tanto per confrontarlo con la versione Java, quanto per predisporne l’uso con LuaTeX. Il codice in Lua √® molto meno strutturato di quello in Java e ha un sapore, diciamo cos√¨, pi√Ļ artigianale ma √® pi√Ļ semplice, eccolo:

#!/usr/bin/lua

-- script di traduzione di numeri in lettere
-- nella lingua italiana
-- traduce un numero in lettere

-- funzione ricorsiva
local function NumberToTextRicorsiva(n)
    if n < 0 then 
        return "meno " .. NumberToTextRicorsiva(-n)
    elseif n == 0 then
        return ""
    elseif n <= 19 then
        local ventina = { "uno", "due", "tre", "quattro", "cinque", 
                          "sei", "sette", "otto", "nove", "dieci", 
                          "undici", "dodici", "tredici", 
                          "quattordici", "quindici", "sedici", 
                          "diciassette", "diciotto", "diciannove" }
        return ventina[n]

    elseif n <= 99 then
        local decine = { "venti", "trenta", "quaranta",
                         "cinquanta", "sessanta", 
                         "settanta", "ottanta", "novanta" }
        local letter = decine[math.floor(n/10)-1]
        local t = n % 10
        if t == 1 or t == 8 then
            letter = string.sub(letter,1,-2)
        end
        return letter .. NumberToTextRicorsiva(n % 10)
    elseif n <= 199 then
        return "cento" .. NumberToTextRicorsiva(n % 100)
    elseif n <= 999 then
        local m = n % 100
        m = math.floor(m/10)
        local letter = "cent"
        if m ~= 8 then
           letter = letter .. "o"
        end
        return NumberToTextRicorsiva( math.floor(n / 100)) ..
               letter ..
               NumberToTextRicorsiva(n % 100)
        elseif n<= 1999 then
            return "mille" .. NumberToTextRicorsiva(n % 1000)
        elseif n<= 999999 then
            return NumberToTextRicorsiva(math.floor(n / 1000)) ..
                   "mila" ..
                   NumberToTextRicorsiva(n % 1000)
        elseif n <= 1999999 then
            return "unmilione" .. NumberToTextRicorsiva(n % 1000000)
        elseif n <= 999999999 then
            return NumberToTextRicorsiva(math.floor(n / 1000000))..
                   "milioni" ..
                   NumberToTextRicorsiva(n % 1000000)
        elseif n <= 1999999999 then
            return "unmiliardo" .. NumberToTextRicorsiva(n % 1000000000)
        else return NumberToTextRicorsiva(math.floor(n / 1000000000)) ..
                    "miliardi" ..
                    NumberToTextRicorsiva(n % 1000000000)
    end
end

-- funzione wrapper
local function NumberToText(n)
   if n == 0 then
     return "zero"
  else
     return NumberToTextRicorsiva(n)
  end
end

-- prelevo il valore numerico come argomento dello script
local num = arg[1]

-- gestisco il caso di numero con decimali
local dec = num - math.floor(num)
local tail = ""

if dec ~= 0 then
   dec = dec * 100
   tail = "/" .. string.format("%.0f",dec)
end

print("in lettere: ".. NumberToText(math.floor(num))..tail)

Soluzione in LuaTeX

Adesso vediamo come incorporare il programma in un documento LuaTeX, ovvero in un file di testo che passato come argomento al motore di composizione tipografica LuaTeX, produce un file pdf con il contenuto voluto.

Come per gli altri motori tipografici della famiglia TeX, anche in LuaTeX il testo viene direttamente composto nel documento a meno che non sia preceduto dal carattere di backslash: “\”. Questo particolare carattere viene interpretato come simbolo di escape nei confronti del testo che lo segue. Cos√¨ se TeX incontra il testo:

Il numero 123456789 in lettere viene scritto come \traduci{123456789}.

allora inserir√† nel documento finale tutti i caratteri tranne per \traduci che viene inteso invece come una macro. Questo √® il succo di uno dei linguaggi tipografici pi√Ļ noti al mondo.

Dunque baster√† associare la corretta definizione della macro \traduci per veder comparire automaticamente nel pdf il numero tradotto in lettere scritto tra parentesi graffe con il significato di argomento. Il codice Lua deve essere leggermente modificato, primo perch√© l’operatore modulo corrisponde al carattere di commento di TeX per cui lo esprimeremo in termini matematici (si tratta del resto della divisione intera). Secondo, dobbiamo trasformare i simboli di commento Lua nel simbolo di commento TeX e terzo, occorre premettere al simbolo di tilde che compare nell’operatore di disuguaglianza la macro primitiva \string per evitare che venga interpretato diversamente. Ecco il sorgente LuaTeX per intero:

\directlua{
function mod(n,d)
   return n-d*math.floor(n/d)
end
}

\def\traduci#1{%
\directlua{
local function NumberToTextRicorsiva(n)
    if n < 0 then 
        return "meno " .. NumberToTextRicorsiva(-n)
    elseif n == 0 then
        return ""
    elseif n <= 19 then
        local ventina = { "uno", "due", "tre", "quattro", "cinque", 
                          "sei", "sette", "otto", "nove", "dieci", 
                          "undici", "dodici", "tredici", 
                          "quattordici", "quindici", "sedici", 
                          "diciassette", "diciotto", "diciannove" }
        return ventina[n]
    elseif n <= 99 then
        local decine = { "venti", "trenta", "quaranta",
                         "cinquanta", "sessanta", 
                         "settanta", "ottanta", "novanta" }
        local letter = decine[math.floor(n/10)-1]
        local t = mod(n, 10)
        if t == 1 or t == 8 then
            letter = string.sub(letter,1,-2)
        end
        return letter .. NumberToTextRicorsiva(mod(n,10))
    elseif n <= 199 then
        return "cento" .. NumberToTextRicorsiva(mod(n,100))
    elseif n <= 999 then
        local m = mod(n,100)
        m = math.floor(m/10)
        local letter = "cent"
        if m \string~= 8 then
           letter = letter .. "o"
        end
        return NumberToTextRicorsiva( math.floor(n / 100)) ..
               letter ..
               NumberToTextRicorsiva(mod(n,100))
        elseif n<= 1999 then
            return "mille" .. NumberToTextRicorsiva(mod(n,1000))
        elseif n<= 999999 then
            return NumberToTextRicorsiva(math.floor(n / 1000)) ..
                   "mila" ..
                   NumberToTextRicorsiva(mod(n,1000))
        elseif n <= 1999999 then
            return "unmilione" .. NumberToTextRicorsiva(mod(n,1000000))
        elseif n <= 999999999 then
            return NumberToTextRicorsiva(math.floor(n / 1000000))..
                   "milioni" ..
                   NumberToTextRicorsiva(mod(n,1000000))
        elseif n <= 1999999999 then
            return "unmiliardo" .. NumberToTextRicorsiva(mod(n,1000000000))
        else return NumberToTextRicorsiva(math.floor(n / 1000000000)) ..
                    "miliardi" ..
                    NumberToTextRicorsiva(mod(n,1000000000))
    end
end

% funzione wrapper
local function NumberToText(n)
   if n == 0 then
     return "zero"
  else
     return NumberToTextRicorsiva(n)
  end
end

% prelevo il valore numerico come argomento dello script
local num = #1

% gestisco il caso di numero con decimali
local dec = num - math.floor(num)
local tail = ""

if dec \string~= 0 then
   dec = dec * 100
   tail = "/" .. string.format("\%.0f",dec)
end
tex.sprint(NumberToText(math.floor(num))..tail)
}}

Il numero 123456789 in lettere viene scritto come \traduci{123456789}.

Ciao
\bye

Compilatelo dopo averlo inserito in un file chiamato trad.tex e compilatelo con il comando da console: luatex trad (occorre che una distribuzione recente del sistema TeX sia installata sul vostro sistema, per esempio TeX Live).

Se volete potete scaricare da questo link il pdf risultato.

Come breve riferimento, posso aggiungere che nel codice LuaTeX la definizione della macro \traduci si avvale della nuova primitiva \directlua che esegue codice Lua, in cui si possono elaborare argomenti rappresentati con il simbolo #1 (per il primo di essi), proprio come un normalissimo sorgente TeX. Inoltre, la funzione di sostituzione dell’operatore modulo viene caricata in memoria in una prima macro \directlua dimostrando come in LuaTeX le varie porzioni di codice distribuite tra le varie \directlua del sorgente, condividono lo spazio delle variabili globali.

Ci tengo a precisare che l’operazione di traduzione di numeri in lettere √® gi√† stata brillantemente risolta per la lingua italiana dal Prof. Enrico Gregorio con il suo pacchetto itnumpar utilizzando esclusivamente codice TeX. Questo pacchetto tiene conto degli accenti delle parole delle cifre e della eventuale necessit√† dell’iniziale maiuscola. Esso √® stato scritto per l’elaborazione dei titoli di capitolo per esempio per far uscire il testo “Capitolo Diciotto” anzich√© del solito “Capitolo 18”.

Bene. Ho terminato questo post ricco di codice. Come sempre sono aperti i commenti per aggiungere, segnalare o fare domande. Alla prossima.
Ciao.

PostgreSQL gestisce i dati, LuaLaTeX li stampa


Scarica l’articolo in formato pdf per la stampa

Argomentare sulla soluzione migliore

Vi sono moltissime situazioni gestionali in cui ricorriamo a fogli di calcolo o alle stesse directory di file per memorizzare dati e stampare report di analisi e di rendicontazione.
Sappiamo però che lo strumento migliore per definizione per gestire i dati è un database ed il migliore tra i DBMS è PostgreSQL, mentre dal lato della stampa, il migliore programma di composizione tipografica è LaTeX.

In questo post illustreremo passo passo come unire i due mondi, gestione dati con PostgreSQL e stampa di report con LaTeX, il modo migliore possibile per svolgere il compito. Non solo, usufruiremo di un altro gioiello del software libero, il sistema operativo Linux Ubuntu 10.04.

Installazione di PostgreSQL 9

Non farò cenno alla procedura per installare una distribuzione TeX, per esempio una TeX Live perché già oggetto di altri post su questo stesso blog, per esempio il post Installare TeX Live 2010 in Ubuntu Lucid Lynx, ne come si installa Ubuntu stesso.
Cominceremo invece installando PostgreSQL nella versione pi√Ļ recente e potente: la 9.0.3 su un sistema della serie LTS 10.04.

Ad oggi, la versione 9 di PostgreSQL non è ancora disponibile nei repository ufficiali di Ubuntu 10.04 (lo sarà per Ubuntu 11.04 Natty Narwhal, quindi per il prossimo mese di aprile), principalmente per le policy di Debian. Noi possiamo installare tranquillamente la versione 8.4 oppure rivolgersi al repository su Launchpad di Martin Pitt, il maintainer Debian per PostgreSQL.
Baster√† qualche comando da terminale per portare a termine l’operazione.

Si aggiunge il repository di Martin Pitt tra le sorgenti software disponibili sul nostro sistema:

$ sudo add-apt-repository ppa:pitti/postgresql
$ sudo apt-get update
$ sudo apt-get upgrade

e poi si installa l’intero database server con il comando:

$ sudo apt-get install postgresql-9.0

Creare un nuovo utente

Con quest’ultimo semplice comando vengono eseguite numerose attivit√† tra cui la creazione dell’utente postgres per la gestione in sicurezza del DB, a cui si pu√≤ accedere soltanto se si √® l’amministratore del sistema.

Ma perché si interagisce con PostgreSQL per mezzo di credenziali utente?

PostgreSQL si basa sulla struttura di rete client-server: un programma, detto server, accetta richieste di rete solo se provenienti da utenti autorizzati, mentre un programma, detto client, invia le richieste al recapito del server ed eventualmente riceve la risposta.

Nel nostro caso, il server ed il client girano sullo stesso PC ma, ovviamente, niente vieta che con le dovute impostazioni di sicurezza e di rete, il server con l’intero complesso di dati possa essere contattato da qualsiasi altro computer connesso al web (per di pi√Ļ indipendentemente dal sistema operativo della macchina client). In particolare √® diffusissimo il caso in cui la tecnologia client-server venga sfruttata per fornire servizi dati ai PC di una rete locale LAN basata sul protocollo della rete internet.

Dunque creiamo un nuovo utente autorizzato per l’accesso al server di PostgreSQL, figura che ha un ruolo diverso da quello dell’amministratore ovvero il compito di leggere e scrivere dati da una postazione client e non quello di gestire il server.

Sia luke il nome dell’utente, allora apriamo il terminale e per prima cosa diventiamo l’admin postgres (attenzione, si tratta di un particolare account del sistema operativo), l’unico (per il momento) in grado di accedere al DB server:

$ sudo su postgres

e creiamo l’utente con password (-P) da conservare in forma criptata (-E) con un comando scorciatoia (digitate man createuser oppure createuser –help per la spiegazione delle opzioni). Ecco la sessione di lavoro con il comando createuser (confronta anche l’immagine sottostante):

postgres$ createuser -P -E
Inserisci il nome dell'utente da aggiungere:luke
Inserisci la password per il nuovo utente:
Conferma password:
Il nuovo utente dev'essere un superuser? (s/n) n
Il nuovo utente può creare database? (s/n) n
Il nuovo utente può creare altri utenti? (s/n) n
postgres@roberto-desktop:/home/roberto$ exit
$
Adding a new user in PostgreSQL from terminal

Adding a new user in PostgreSQL from terminal

Adesso potete svestire i panni dell’utente ‘postgres’, digitando semplicemente ‘exit’ (se qualcosa va storto, potete eliminare l’utente con il comando dropuser luke e ricominciare da capo).

Il server riceve la richiesta e crea il nuovo utente di lavoro, ma non abbiamo ancora terminato perch√© al nuovo ruolo dobbiamo assegnare l’autorizzazione necessaria. Per farlo editiamo con i diritti di amministratore il file di configurazione pg_hba.conf:

$ cd /etc/postgres/9.0/main
$ sudo gedit pg_hba.conf

Attenzione, l’operazione di modifica √® delicata! Aggiungete, come vedete nella schermata la riga ‘local all luke md5’ sotto la riga gi√† presente per l’utente postgres come segue:

# Database administrative login by UNIX sockets
local   all         postgres                          ident
local   all         luke                              md5

Abbiamo reso possibile le connessioni da rete locale per tutti i database presenti sul server (chiave ‘all’) per l’utente ‘luke’. Inoltre la password transiter√† in rete locale in forma criptata con l’algoritmo md5 (consultare la documentazione di PostgreSQL al capitolo Client Authentication disponibile sul sito alla sezione Documentation).

Per far si che abbia effetto la modifica, riavviate il server da utente ‘postgres’ con i comandi (non pu√≤ certo farlo un utente normale):

$ sudo su postgres
postgres$ /etc/init.d/postgresql restart
postgres$ exit
$
Editing pg_hba.conf

Editing pg_hba.conf

Se le cose, vi sembrano complicate √® pi√Ļ che normale, il server di PostgreSQL non √® certo un piccolo programma desktop, e porta con se molti nuovi concetti da imparare. In pi√Ļ si crea una certa confusione per il fatto che lavoriamo su un unico computer e con diverse identit√† utente.

Creare il nostro database

Il server pu√≤ gestire molti database contemporaneamente. Per crearne uno √® sufficiente sceglierne il nome e lanciare il comando seguente come utente ‘postgres’ (‘createdb –help’ per consultare le informazioni di base):

$ sudo su postgres
postgres$ createdb ricDB
postgres$ exit

Il problema

Come esempio, ho scelto una situazione semplice in cui è necessario emettere molti documenti periodici: il rilascio di ricevute di pagamento.
Ogni ricevuta ha un numero progressivo che si riazzera ogni anno, un ammontare, una data di emissione, e la data di scadenza.
Esprimiamo tutto ci√≤ in linguaggio SQL (per documentarsi un po’ ancora la documentazione di PostgreSQL √® un ottima fonte, al capitolo Tutorial):

CREATE TABLE receipts (
progrnum      integer,                -- numero mese ricevuta nell'anno
yearnum       integer,                -- anno ricevuta
amount        numeric(16,4) NOT NULL, -- importo (4 decimali e 16 cifre totali)
duedate       date          NOT NULL, -- data di scadenza
paymentdate   date,                   -- data di pagamento
receiptyn     boolean,                -- consegna al cliente avvenuta si/no
PRIMARY KEY (progrnum, yearnum)
);

ALTER TABLE receipts ADD
   CONSTRAINT month_great_than_zero
   CHECK (progrnum>0);

ALTER TABLE receipts ADD
   CONSTRAINT year_great_than_zero
   CHECK (yearnum>0);

-- inserimento di alcune ricevute di esempio
INSERT INTO receipts
(prognum, yearnum, duedate, paymentdate, amount, receiptyn)
VALUES
( 1, 2010, '2010-06-15', '2010-06-15',  159.87, 'y'),
( 2, 2010, '2010-06-15', '2010-06-16',  180.30, 'y'),
( 3, 2010, '2010-06-15', '2010-06-20',  780.99, 'y'),
( 4, 2010, '2010-07-10', '2010-08-10',  139.14, 'y'),
( 5, 2010, '2010-07-15', '2010-08-23',  800.90, 'y'),
( 6, 2010, '2010-08-01', '2010-08-01',  111.70, 'y'),
( 7, 2010, '2010-08-28', '2010-08-23',  202.50, 'y'),
( 8, 2010, '2010-09-30', '2010-09-27',  300.41, 'y'),
( 9, 2010, '2010-09-30', '2010-09-27',  100.91, 'y'),
(10, 2010, '2010-10-15', '2010-10-25',   59.87, 'y'),
(11, 2010, '2010-11-10', '2010-11-23', 1059.87, 'y'),
( 1, 2010, '2010-12-01', '2010-12-15',   19.10, 'y'),
( 2, 2011, '2011-01-08', '2011-01-12',   59.80, 'y');

Avremo dovuto in realt√† prevedere anche un nominativo con una chiave esterna verso una seconda tabella, ma l’esempio si complicherebbe oltre i nostri obiettivi.

Usare il client psql

Copiate il codice SQL precedente in un file di testo e chiametelo ‘run.sql’. Lo eseguiremo per intero in un colpo solo utilizzando il client a riga di comando fornito da PostgreSQL dal breve nome di psql. Questo √® un compito per il nostro luke (la directory di lavoro del terminale deve essere quella contenente il file a meno di non speficiare per intero il path di ‘run.sql’, mentre il siglificato delle opzioni ricavabile da un banale ‘psql –help’ dovrebbe comunque essere chiaro):

$ psql -U luke -d ricDB -f run.sql

Subito qualche analisi sui dati

Il client ‘psql’ pu√≤ essere anche in modalit√† interattiva. Dovremo specificare solamente l’utente con cui connettersi al server ed il database su cui vogliamo lavorare:

$ psql -U luke -d ricDB

Una volta entrati in modalit√† interattiva, possimao impartire direttamente i comandi SQL anche su pi√Ļ righe. Leggiamo subito la versione di PostgreSQL con cui stiamo lavorando digitando (attenzione, se l’output √® troppo esteso per le dimensioni della finestra del terminale i risultati sono mostrati in uno speciale modo da cui si esce per tornare al prompt interattivo premendo il tasto ‘q’, inoltre non dimenticatevi il punto e virgola finale):

ricDB=>SELECT version();

Per consultare l’intera tabella

ricDB=> select * from receipts;
  progrnum | yearnum |  amount   |   duedate  | paymentdate | receiptyn
-----------+---------+-----------+------------+-------------+-----------
         1 |    2010 |  159.8700 | 2010-06-15 | 2010-06-15  | t
         2 |    2010 |  180.3000 | 2010-06-15 | 2010-06-16  | t
         3 |    2010 |  780.9900 | 2010-06-15 | 2010-06-20  | t
         4 |    2010 |  139.1400 | 2010-07-10 | 2010-08-10  | t
         5 |    2010 |  800.9000 | 2010-07-15 | 2010-08-23  | t
         6 |    2010 |  111.7000 | 2010-08-01 | 2010-08-01  | t
         7 |    2010 |  202.5000 | 2010-08-28 | 2010-08-23  | t
         8 |    2010 |  300.4100 | 2010-09-30 | 2010-09-27  | t
         9 |    2010 |  100.9100 | 2010-09-30 | 2010-09-27  | t
        10 |    2010 |   59.8700 | 2010-10-15 | 2010-10-25  | t
        11 |    2010 | 1059.8700 | 2010-11-10 | 2010-11-23  | t
         1 |    2010 |   19.1000 | 2010-12-01 | 2010-12-15  | t
         2 |    2011 |   59.8000 | 2011-01-08 | 2011-01-12  | t
(13 rows)
ricDB=>

Per trovare il totale dei pagamenti digitate:

ricDB=> select sum(amount) as totale from receipts;

Per vedere i giorni di ritardo od anticipo rispetto alla data di scadenza, e per calcolarne la media digitare:

ricDB=> select duedate-paymentdate as diff from receipts;
ricDB=> select avg(duedate-paymentdate) as average_diff from receipts;

In ambiente reale, vi sarebbe la necessita di scrivere uno script interattivo per esempio per facilitare l’inserimento dei dati di una ricevuta, piuttosto che utilizzare direttamente il linguaggio SQL.
Bene. Adesso possiamo uscire dal client ‘psql’ digitando \q, e passare al reporting dei dati.

Creare un report con LuaLaTeX

Breve definizione di LuaLaTeX

Nel tempo sono stati sviluppati diversi motori di composizione tipografica a partire dal progenitore TeX. Questi programmi elaborano un file di testo detto sorgente, scritto nella corretta sintassi, in un processo chiamato compilazione per produrre un file di output contenente il risultato tipografico, normalmente in formato PDF. L’ultimo nato in casa TeX ed ancora in fase di sviluppo √® LuaTeX che oltre a riconoscere il linguaggio TeX esteso comprende anche la capacit√† di eseguire codice Lua, un linguaggio di scripting semplice e generale. Ed √® proprio questa la caratteristica che ci permette di connetterci al database direttamente da un sorgente come dimostreremo tra poco.

I motori di composizione fin qui citati, TeX, pdfTeX, LuaTeX, contengono istruzioni molto specializzate per compiti tipografici di dettaglio e quindi poco adatti per un uso di produzione, cos√¨ ben presto sono nati ulteriori linguaggi macro di pi√Ļ alto livello, implementati con le funzioni primitive, il pi√Ļ conosciuto dei quali √® senz’altro LaTeX. Ebbene il formato di alto livello equivalente di LaTeX per LuaTeX √® stato chiamato LuaLaTeX.
Anche qui, vi faccio notare che se non avete capito bene cosa sono questi motori tipografici, non sorprendetevi poich√© vi ho appena condensato pi√Ļ di 32 anni di storia di sviluppo di questi programmi. Per un esposizione pratica potete ulteriormente riferirvi alle due pagine teoria e pratica, del blog, oltre che naturalmente al sito italiano del GuIT.

La regola fondamentale

L’idea di scrivere qualche comando in un file sorgente per LuaLaTeX per eseguire una query verso un database server √® molto interessante e di fattibilit√† tanto recente che non √® ancora chiaro quale ne sia il potenziale applicativo. Si deve tuttavia rispettare una regola, la regola che rispettano tutti i programmi di reporting e che in LuaLaTeX si pu√≤ potenzialmente violare:

Se si sta costruendo un report, eseguire sul database esclusivamente operazioni di lettura!

Rispettate questa regola e avrete molti problemi in meno ed un flusso di lavoro pi√Ļ efficiente.

Installare e configurare LuaSQL

L’accesso al database da LuaLaTeX √® possibile perch√© esiste una libreria che possiamo caricare all’interno del sorgente: LuaSQL, altrimenti dovremo scrivere parecchio codice in C.
Per installarne la versione adatta per PostgreSQL, in Ubuntu c’√® il solito immancabile comando da terminale:

$ sudo apt-get install liblua5.1-sql-postgres-2

Avvenuta l’installazione della libreria occorre indicare a LuaTeX, che entra in azione dietro le quinte quando si lancia LuaLaTeX, dove si trova la libreria LuaSQL, modificando (con i diritti di amministratore trovandosi in directory di sistema non modificabili dall’utente ordinario) un file di configurazione chiamato ‘texmf.cnf’. Ipotizzando che sia installata una distribuzione TeX Live 2010 ecco i comandi di console per aprire in modifica il file con l’editor di sistema ‘Gedit’:

$ sudo gedit /usr/local/texlive/2010/texmf/web2c/texmf.cnf

Cercate la linea che definisce la variabile CLUAINPUTS (dovrebbe essere intorno alla riga 400) e modificatela per aggiungerci il percorso ‘/usr/lib/lua’ (fate attenzione a non modificare il resto della definizione e non dimenticatevi di specificare il doppio slash finale che indica di esplorare anche le subdirectory):

% Lua needs to look for binary lua libraries distributed with packages.
CLUAINPUTS = .;/usr/lib/lua//;$SELFAUTOLOC/lib/{$progname,$engine,}/lua//

Finalmente pronti

Giunti fin qui dopo aver dettagliatamente descritto i passi necessari per la costruzione del sistema, ci dedichiamo finalmente al nostro sorgente LuaLaTeX. Comprendere il linguaggio Lua non √® difficile grazie alla bravura e alla lungimiranza dei suoi creatori, il codice √® semplice. Per saperne comunque di pi√Ļ non c’√® niente di meglio che studiarsi il PiL, acronimo del titolo del libro ‘Programming in Lua’.

Nel listato che segue, un sorgente elementare in LuaLaTeX, lo scopo √® quello di creare un unica ricevuta, per esempio la n. 8 del 2010. All’interno del comando \directlua si trova il codice in Lua che per prima cosa si connette al database con le credenziali dell’utente ‘luke’ e poi esegue la query per ricavare i dati della ricevuta. Il risultato della query √® una tabella Lua in cui i nomi delle chiavi corrispondono ai nomi dei campi della tabella receipts del database.
L’accesso alla tabella restituita dalla query avviene tramite un oggetto cursor, al cui metodo fetch() √® passata una tabella e un parametro che indica di creare le chiavi con i nomi dei campi e non con il loro semplice indice numerico.
Per rendere disponibile a TeX i dati, vengono create con l’ausilio della funzione tex.sprint() interna alla funzione makecmd(), normalissime macro.
Nel sorgente ricordatevi di sostituire alla riga 8 la ‘password’ che avete scelto al momento della creazione dell’utente ‘luke’.

\documentclass{minimal}

% connessione al server PostgreSQL
%
\directlua{
  require "luasql.postgres"
  local env = assert(luasql.postgres())
  local con = assert(env:connect("ricDB","luke","password"))

  local cur = assert(con:execute(
  "SELECT * FROM receipts WHERE prognum=1 AND yearnum=2011"))
  local result = cur:fetch({},"a")

  cur:close()
  con:close()
  env:close()

  local function makecmd( cmd, val)
     local bs = "\string\\"
     tex.sprint(bs.."def"..bs..cmd.."{"..val.."}")
  end

  makecmd("anno",    result.yearnum)
  makecmd("numric",  result.prognum)
  makecmd("importo", result.amount)
  makecmd("dataric", result.paymentdate)
  makecmd("scadenza",result.duedate)
}

\begin{document}
\hspace*{5cm}Ricevuta n. \numric /\anno del \dataric

Si rilascia ricevuta di pagamento per la somma di euro \importo,
avvenuto in data \dataric{} con scadenza in data \scadenza.

Distinti Saluti
\end{document}

Conclusioni

I software utilizzati sono potenti e complessi, e pongono pochi limiti all’utente che, proprio per questo, deve investire parecchie risorse per padroneggiarli ma ci√≤ non esclude un utilizzo della procedura descritta nell’ambito reale, anche se non si √® un esperto Database Administrator o un guru di TeX. Vantaggi e limiti risiedono nel completo controllo sulla nostra soluzione.

Nell’ambito di sviluppo invece, il post dimostra come sia possibile implementare un linguaggio specifico per i documenti TeX per consentire all’utente nuove potenti applicazioni in ambito aziendale usufruendo di una gestione centralizzata dei dati sulla rete locale.
Per esempio non è difficile immaginare servizi di gestione documentale, dove il team di colleghi condivide le informazioni producendo documenti PDF di elevata qualità con TeX e con il massimo di disponibilità della fonte dati con PostgreSQL. Efficienza, accuratezza, potenza di analisi e controllo.

Leggere la directory corrente con lualatex


Certi compiti difficili…

In LaTeX si sa, certi compiti sono particolarmente difficili, altri, forse, impossibili. Uno di questi √® inserire nel documento la directory corrente. Non che sia indispensabile, anzi, direi che leggere il path del file nel documento solo perch√© si pensa che un giorno possa essere utile per ritrovare il file, penso sia poco elegante. Comunque…

Risolvere con lualatex

Compilare un sorgente LaTeX con lualatex comporta solo lievi modifiche, e la possibilità di far eseguire codice Lua attraverso la nuova primitiva \directlua{}.
Di per se Lua non offre sofisticate librerie per l’accesso al disco, cos√¨ occorrerebbe installare la Lua File system Library installabile in Ubuntu con il relativo pacchetto nel repository universe chiamato liblua5.1-filesystem0, ed in Windows con il programma LuaRocks.
Tuttavia LuaTeX viene compilata assieme alla libreria Lua Filesystem, così è possibile utilizzarla senza che si debba installare esplicitamente.

Le funzioni della libreria lfs sono riportate nel manuale disponibile on-line su questo sito, e sono tutte memorizzate nella tabella lfs. Per i nostri scopi utilizzeremo la funzione currentdir(), che restituisce il path della directory corrente, sempre che non ci siano errori.

Riporto il codice di un sorgente LaTeX da compilare con lualatex che conterrà il percorso dello stesso file sorgente, ma che funziona solo con Linux come spiegherò fra poco.

% funziona per Linux
\documentclass{minimal}

\newcommand{\curdir}{%
   \directlua{tex.sprint(lfs.currentdir())}
}

\begin{document}
\curdir
\end{document}

Il post non vuole altro che segnalare quanto compiti assai complicati e dispendiosi in termini di tempo per trovare una soluzione, siano semplici ed eleganti con lualatex, che si basa sul nuovo motore di composizione ancora in fase di sviluppo LuaTeX.

Ed √® per questo che √® stata riportata la soluzione per Linux, perch√© quella per Windows √® leggermente pi√Ļ complicata: occorre sostituire nel percorso del file il carattere backslash perch√© se il percorso della directory corrente entra cos√¨ com’√® nel flusso di token, verr√† interpretata come una serie di comando che produrranno un errore.

Nei file system di tipo Unix, il carattere che separa i nomi √® la slash e non ci sono quindi problemi. In Window se per esempio il path della directory di lavoro √® “C:\Documenti” corrisponder√† ai caratteri C: seguiti dalla macro \Documenti, un bel inconveniente.

Ecco il codice per Windows (e che funziona perfettamente anche in Linux perch√© il path rimarr√† immutato). Nel codice dovendo inserire il carattere di escape del motore di composizione se ne evita l’effetto utilizzando la macro primitiva string.

% funziona anche in Windows
\documentclass{minimal}

\newcommand{\curdir}{%
    \directlua{
    require "lfs"
    local cd = string.gsub(lfs.currentdir(),
              [[\string\]],
	      [[\string\textbackslash ]])
   tex.sprint(cd)}
}

\begin{document}
\curdir
\end{document}

Rovesciare il punto di vista

Il comando \curdir cos√¨ come l’abbiamo definito ha un difetto di efficienza: ogni volta che lo eseguiamo, con la libreria lfs determina la directory corrente, ovvero qualcosa che in realt√† conoscevamo dalla prima chiamata alla macro.
Se per esempio nel documento il percorso del file viene inserito a piè di pagina, avremo tante chiamate a lfs.currentdir() tante quante sono le pagine stesse, ma se rovesciassimo il punto di vista sfruttando proprio il problema del backslash?

Nel seguente codice, completo per la compilazione con lualatex, si esegue la funzione per determinare il path e poi si inserisce nel flusso dei token che saranno “digeriti” dal motore tipografico, la definizione di una macro che espande semplicemente al path stesso.
In questo modo, la funzione di libreria viene chiamata solo una volta, mentre nel documento l’autore si trover√† una macro standard, espandibile ed efficiente.
Per prima cosa, ho racchiuso il codice Lua in un blocco do … end, per eliminare dalla memoria qualsiasi variabile, poi ho memorizzato per comodit√† in una variabile stringa chiamata bs il carattere di backslash.
La definizione di una macro che espande al path √®: \def\curdir{valore del path}, cos√¨ √® sufficiente chiamare la funzione tex.sprint() con l’equivalente del comando, utilizzando l’operatore di concatenazione delle stringhe di Lua sintatticamente definito da due punti .. .

\documentclass{minimal}

\directlua{
    do
       require "lfs"
       local bs = "\string\\"
       local cd = string.gsub(lfs.currentdir(),
              bs,
	      bs.."textbackslash ")

       tex.sprint(bs.."def"..bs.."curdir{"..cd.."}")
    end
}

\begin{document}

\texttt{\curdir}

\emph{\curdir}

\curdir

\curdir

\curdir

\end{document}

Saluti ed Auguri di Buon Natale e Felice Anno nuovo!!!

Trailing spaces


Deblanking

Non √® affatto semplice in TeX eliminare i caratteri spazio iniziali e finali rispetto ad un testo ed immessi come argomento ad un comando. Spesso vorremmo che il comportamento sintattico di classi e pacchetti fosse appunto quello di non considerare questi caratteri spazio, cos√¨ da poter scrivere il codice pi√Ļ liberamente.
Per esempio scrivere \cool{ il mio nome è nessuno } in modo equivalente a \cool{il mio nome è nessuno}.

Soluzione in Lua

Provate questo codice:

function deblank(s)
local bs = string.match(s, "^%s*")
local es = string.match(s, "%s*$")
return string.sub(s,#bs+1,#s-#es)
end

io.write("string:")
local ans = io.read("*line")

print("<"..ans..">","<"..deblank(ans)..">")

Si tratta di codice Lua con cui si risolve il problema utilizzando i pattern della libreria standard string. Il pattern “^%s*”, significa trova gli eventuali primi caratteri spazio della stringa, mentre quello “%s*$”, sta per trova gli eventuali ultimi blank nella stringa.
L’uso dello script √® semplice: salvate il codice in un file di testo chiamato per esempio “deblank.lua” ed eseguitelo con il comando lua deblank.lua in una finestra di console. Digitando una stringa il programma la stamper√† prima intatta e poi priva dei fastidiosi caratteri spazio.
Leggete se vi interessa la documentazione della libreria string di Lua a questo link.

Aggiunta del 19/12/2010:
Il codice si pu√≤ ulteriormente semplificare. In effetti la domanda generale √®: qual’√® il pattern che restituisce la stringa depurata degli spazi iniziali e finali?
Il pattern “^%s*(.*)%s*$” non funziona perch√© il punto rappresenta qualsiasi carattere ed unito all’asterisco significa una qualsiasi sequenza da zero a pi√Ļ caratteri cos√¨ il pattern non esclude i trailing spaces appartenendo al pattern di ricerca “.*”.
La soluzione √® utilizzare il pattern “.-“ che significa sempre da zero a pi√Ļ caratteri ma la ricerca si ferma alla prima corrispondenza del pattern successivo. In altre parole, la corrispondenza √® ricercata per il minimo numero di caratteri.
La funzione precedente si semplifica quindi nella seguente:

function deblank(s)
return string.match(s,"^%s*(.-)*%s*$")
end

Il pattern giusto √® stato trovato e cos√¨ anche √® stato raggiunto l’obbiettivo iniziale.

Epilogo

La conclusione è questa: il nuovo motore tipografico luatex, è uno strumento dalle enormi potenzialità che potrà portare la qualità tipografica di TeX in innumerevoli nuovi contesti, grazie al piccolo grande linguaggio Lua.

Alla prossima…

Riallineare valori testuali


Dati testuali tabellari

A volte abbiamo la necessità di dover riallineare un testo tabellare inserendo o cancellando caratteri spazio. In questo modo i valori delle celle risulteranno allineati in colonne se stampati con un font a spaziatura fissa tipo macchina da scrivere.

Per intenderci, da così:

123 89.08 33.9 5.90 67.5
8 78.00 4944.9 208.93
567 8.99 99.2 90.89 7890.2
41 18.03 5464.6 38.73 993.3

a così:

123 89.08   33.9   5.90   67.5
  8 78.00 4944.9 208.93
567  8.99   99.2  90.89 7890.2
 41 18.03 5464.6  38.73  993.3

√ą il caso in cui un qualche programma fornisce in output dei file testuali di dati che dobbiamo impaginare per la stampa, oppure, quando √® necessario estrarre dati da un file testuale in cui i campi devono essere individuati con la posizione dei caratteri (il cosiddetto tracciato record).

Naturalmente si pu√≤ fare a mano con un buon editor di testi (che magari facilita il compito permettendo la selezione di aree rettangolari di testo come si pu√† fare con SciTE), ma se le linee sono migliaia…

Impaginare a mano grandi moli di dati non è solo un compito impossibile e soggetto ad inevitabili errori. In fondo anche le normative sulla sicurezza sul lavoro (come il nostro Testo Unico 81/08) impongono di fare tutto il possibile per evitare compiti ripetitivi e noiosi.

La soluzione

Un programma pu√≤ svolgere egregiamente questo compito, ed in passato, chiss√† quante volte ne ho rimpianto la mancanza. Ma adesso che son cresciuto, vi propongo una soluzione grazie a Lua, il linguaggio di scripting pi√Ļ potente della storia!!!

Vai con il codice

Per la parte divertente ci riferiremo dunque a Lua. Lascio a voi il compito eventuale di installare Lua versione 5.1 o successiva sul vostro sistema (su Linux spesso lo trovate già pronto).

#!/usr/bin/lua
-- read the text file line by line
-- and reformatting the spacing about numbers

-- if no output filename is available, one is provided
local filename = arg[1]
local filenameResult = arg[2]

if not arg[2] then
   local i, j, ext = string.find( filename , "%.([^%.]*)$" )
   if ext then
      filenameResult = string.sub(filename, 1, i - 1) .. "_result." .. ext
   else 
      filenameResult = filename .. "_result"
   end
end

local f = assert(io.open( filename , "r" ))
local words = {}
local coldim = {}

while true do
   local line = f:read("*line")
   if line == nil then break end

   j = 0
   k = 1      -- a words counter in a line
   t = {}
   while true do
      i,j = string.find(line, "%S+", j+1)
      if i == nil then break end
      
      -- a word must be processed
      local w = string.sub(line, i, j )
      t[ k ] = w
      local dim = #w
      if coldim[k] == nil then
          coldim[k] = dim
      elseif coldim[k] < dim then
          coldim[k] = dim
      end
      k = k + 1
   end
   words[ #words + 1 ] = t
end

f:close()

local f = assert(io.open( filenameResult ,"w"))

for i,v in ipairs( words ) do
   local rebuildedLine = ''
   for j,w in ipairs( v ) do
      rebuildedLine = rebuildedLine ..
         string.rep(" ", coldim[j]-#w) ..
            ((j>1) and ' ' or '' ) .. w
   end
   f:write(rebuildedLine .. '\n')
end

f:close()
-- end

Il codice si sviluppa dapprima memorizzando nella tabella words le tabelle v contenenti le “parole” (sequenze di caratteri non contenenti spazi). La ricerca delle parole viene risolta utilizzando una funzione della libreria di Lua chiamata string.find che accetta un pattern di ricerca (il pattern “%S” con la s maiuscola, indica un carattere diverso da un blanck, “%S+” indica uno o pi√Ļ caratteri diversi da blanck).

Cos√¨ elaborato l’intero file, si procede a ricostruire il file risultato ordinatamente linea per linea eseguendo un doppio ciclo for sulla tabella di tabelle. La funzione string.rep (sempre della libreria di Lua), restituisce una stringa ripetendo quella di input per il numero di volte voluto. Quindi √® possibile “giustificare” a destra le parole anteponendovi il giusto numero di spazi.

Si ma come si fa a sapere “il giusto numero di spazi”? In un ulteriore tabella memorizzata in coldim viene memorizzato colonna per colonna la massima lunghezza della parola.

Ulteriore notizia riguarda l’operatore # disponibile dalla versione 5.1 di Lua, che restituisce la dimensione dell’oggetto che segue, quindi la lunghezza di una stringa od le dimensioni di un array (una tabella indicizzata con i numeri interi).

Ricordiamoci che in Lua l’unica struttura dati disponibile √® la tabella, ovvero un array associativo dove la chiave pu√≤ essere un numero od una stringa. Per instanziare un oggetto tabella l’unico modo √® ricorrere al costrutture che nella forma pi√Ļ semplice √® questo {} che crea una tabella vuota e ne restituisce il riferimento che possiamo assegnare ad una variabile.

Se volete √® certamente consigliabile studiarsi il libro “Programming in Lua”, citato anche in questo mio post.

Via al codice

Per lanciare lo script salvare il codice in un file con il nome ftab.lua (renderlo eseguibile se nessario), aprire un terminale e digitare il comando:

./ftab.lua nomedelfileditesto
oppure
lua ftab.lua nomedelfileditesto

Bene. Se vi ho risparmiato un mese di lavoro fatemelo sapere!!! Vi manderò il conto.

Ps. naturalmente il codice pu√≤ essere esteso, per esempio lasciando scegliere all’utente il numero di spazi bianchi tra le colonne del file di output, oppure giustificando “a destra” i numeri ed “a sinistra” le stringhe, od ancora predisporlo per l’elaborazione di file CSV (Comma Separated Value), o per riallineare ambienti tabular in un sorgente LaTeX.
Lua è tremendamente potente.

Differenza tra due date con LuaTeX


Il problema

Alcune volte ci troviamo a dover calcolare la differenza in giorni che intercorre tra due date. Se stiamo lavorando in un Word processor occorre rivolgersi ad uno strumento esterno come un foglio di calcolo, mentre con gli strumenti di tipografia asincrona (LaTeX e compagnia), linguaggi naturalmente estensibili, una macro risolverebbe brillantemente questo problema.

Una soluzione in Lua

I linguaggi come LaTeX o ConTeXt sono attualmente basati su TeX, il motore di composizione di Donald Knuth, perfetti per produrre documenti di elevata qualità, ma proprio per questo, non così facili da programmare.

Da qualche tempo il mondo TeX si sta preparando ad accogliere un nuovo motore di composizione basato sia su TeX stesso sia su Lua, un efficiente linguaggio di scripting portabile ed estensibile a sua volta: luatex.

Attualmente luatex è in fase di sviluppo, ma se sul vostro sistema è installata una distribuzione recente di LaTeX come la TeX Live 2009, probabilmente è già a vostra disposizione.

Utilizziamo la funzione os.time() della operating system library di Lua per tradurre le date in un unico valore numerico, il numero di secondi trascorsi da un certo epoch. A questo punto basta fare la differenza ed esprimere il risultato in giorni anziché in secondi ed il gioco è fatto. Ecco il frammento di codice:

local date1 = os.time({day=1,month=1,year=2010})
local date2 = os.time({day=28,month=7,year=2010})
local secperdays = 24*60*60
local daysdiff = (date2 - date1) / secperdays
print("Days difference: " .. daysdiff)

La funzione os.time() accetta una tabella con le tre chiavi day, month ed year. Nel codice abbiamo dunque racchiuso tra parentesi graffe la tabella nella constructor expression prevista dal linguaggio Lua.

Una soluzione in luatex

In luatex scriveremo dunque una macro utilizzando il classico comando \def per cui si prevede due argomenti: la data precedente e poi quella successiva dell’intervallo temporale:

\def\datediff#1#2{\directlua{
    local date1 = os.time({#1})
    local date2 = os.time({#2})
    local secperdays = 24*60*60
    local daysdiff = (date2 - date1) / secperdays

    tex.print(math.floor(daysdiff))}}

% inizio documento
Dalla mia nascita ad oggi sono trascorsi esattamente
\datediff{year=1900,month=1,day=1}{year=2010,month=1,day=10}
giorni.

Eppure mi pare un numero del tutto privo di emozioni...
\end

Salvate il codice in un file con estensione .tex e compilate il tutto con il comando:

luatex nomefile.tex

Nella directory trovere il file pdf di output con il risultato.
Le funzioni della libreria standard di Lua sono facili da usare e generalmente il codice √® compatto ed efficiente. Cos√¨ √® facile prevedere una stagione ancora pi√Ļ ricca per gli utenti di LaTeX e compagnia.

Alla prossima. Gulp.

%d blogger hanno fatto clic su Mi Piace per questo: