Robitex's Blog

Ideas in the web

Archivi Categorie: Lua

Se SQLite vi sembra lento…


Sommario

Indagine sul perché SQLite fosse così lento ad inserire i dati…
In un progetto di elaborazione dati ho utilizzato un database SQLite per gestire un set di dati abbastanza complesso. Fino ad ora ammettevo che il codice per l’inserimento dei dati fosse molto lento contrariamente all’esecuzione delle query…

Cosa ho vantaggiosamente scoperto

L’inserimento dei dati avviene in SQLite all’interno di transazioni che hanno lo scopo di salvare i dati in modo sicuro. Se eseguo un inserimento tramite la normalissima funzione SQL INSERT, SQLite attende che ogni record inserito sia completamente e con successo salvato fisicamente sul disco.
Questo comportamento è dovuto al fatto che l’istruzione viene inserita automaticamente in una transazione.

Il numero di transazioni che SQLite è in grado di completare è dell’ordine di 10 al secondo, ed ecco spiegato il motivo di quelle strane attese. All’interno di una transazione invece il numero di inserimenti è dell’ordine di 50.000 al secondo.

Soluzioni

Compreso che ogni inserimento crea implicitamente una transazione, le alternative sono due:

  1. non fare niente e lasciare che i dati vengano inseriti con una transazione alla volta per usufruire della massima affidabilità;
  2. rendere le operazioni molto più veloci operando all’interno di un’unica transazione, ma anche meno sicure in caso di guai con il processo client.

…naturalmente ho preferito la seconda…

Giusto per fissare le idee

Giusto per fissare le idee, il codice che ho utilizzato, scritto in Lua e che utilizza la libreria LJSQLite3 scritta per luajit, è il seguente:

-- carico la libreria LJSQLite3
local sql = require "ljsqlite3"

-- creo la connessione
-- (in dbfilename è salvato il nome effettivo del file)
    local conn = sql.open( dbfilename, "rw")

-- abilito il vincolo di chiave esterna
-- per il momento non ON di default per mantenere
-- compatibilità con versione precedenti di SQLite3 versione 3.6
    conn:exec("PRAGMA foreign_keys = ON;")

-- creo una transazione
    conn:exec "BEGIN" -- begin transaction

-- codice per inserire i dati nel database
-- per esempio con un prepared statement
-- (codice esemplificativo con nomi tabella e campi per riferimento)
   local stmt = conn:prepare("INSERT INTO mytable " ..
     .. "(field_1, field_2, ..., field_n)" ..
     .. " VALUES (?, ?, ..., ?);" )
...
...
... eccetera eccetera

-- chiudo la transazione
    conn:exec "END"

-- chiudo la connessione
    conn:close()

Saluti e buona estate.

Annunci

Iterare su SQLite con Lua


Sommario

Riferendoci alla libreria LJSqlite3 verrà spiegato come iterare facilmente la collezione di dati di un database relazionale attraverso una query. Verranno inoltre messe in evidenza le proprietà dell’iteratore ai fini della pulizia e dell’intuitività del codice Lua.

Effective code

È sempre molto utile scrivere codice che sia espressivo e compatto, qualità che potremo riassumere in un unico termine idiomatico: effective.
Gli iteratori di Lua, che abbiamo già visto in questo post, sono costrutti sintattici che semplificano il nostro codice facendo corrispondere un concetto/entità con un elemento del linguaggio.
Lo sviluppatore costruisce così strutture concettualmente vicine a ciò che asserisce al problema da risolvere.

L’ambiente di lavoro

Utilizzeremo il database SQLite3 per creare, inserire ed interrogare i dati, e la libreria LJSqlite3 per LuaJIT un compilatore Lua, per creare il codice applicativo.
Sul vostro sistema dovranno essere installati sia SQLite3, che LuaJIT, e le librerie Xsys e LJSQLite3 scaricabili dai rispettivi siti e disponibili per quasi tutti i sistemi operativi.

Inoltre lavoreremo con un (semplice) database in modalità in-memory cioé interamente mantenuto nella memoria centrale, così che tutto il codice di esempio possa trovare posto in un unico file.

L’iteratore Lua

Il nostro obiettivo è scrivere un iteratore ‘stateless‘ in Lua che ad ogni ciclo ci metta a disposizione record per record, il risultato di una query.
Chiamando l’iteratore ‘iselect()‘ potremo prevedere per esso due argomenti: la connessione al database e la query da eseguire. Per concretizzare l’idea, lasciando i dettagli ad un secondo momento, il codice per stampare l’intero contenuto della tabella di esempio ‘val‘ con i suoi attributi ‘id‘ e ‘num‘ potrebbe essere il seguente:

for i, tr in iselect(conn, "SELECT * FROM val;") do
    print( 'Row '..i..': ', tr.id, tr.num)
end

L’iteratore ad ogni ciclo del ‘generic for’ ritorna il progressivo del record e la ‘tupla’ (una tabella Lua ovviamente) con i valori dei campi indicizzati con chiavi corrispondenti ai nomi degli attributi della tabella del database.

Passiamo ora al codice completo dell’esempio con l’implementazione dell’iteratore definito precedentemente. Non spiegherò il listato nei dettagli tecnici rimandando il lettore alle documentazioni dei singoli componenti peraltro scritte piuttosto bene. Eseguire il codice con il comando $ luajit nomefile.lua.

-- iterator function
local function nextrec(t, i)
    i = i + 1
    if i <= t.__number_of_records then
        local tr = {}
        local nc = #t[0]
        for col=1, nc do
            local field = t[0][col]
            tr[field] = t[col][i]
        end
        return i, tr
    end
end

-- stateless iterator
local function iselect(connection, query)
    local t, nr = connection:exec(query, 'hi')
    t.__number_of_records = nr
    return nextrec, t, 0
end

-- SQL code

local sql = require "ljsqlite3"
-- Open a temporary in-memory database.
local conn = sql.open("")

-- Execute SQL commands separated by the ';'
conn:exec[[
CREATE TABLE val (id TEXT, num REAL);

INSERT INTO val VALUES('myid1', 260);
INSERT INTO val VALUES('myid2', 200);
INSERT INTO val VALUES('myid3', 200);
INSERT INTO val (id) VALUES('myid4');
INSERT INTO val (num) VALUES(200);
INSERT INTO val VALUES('myid6', 260);
INSERT INTO val VALUES('myid7', 201);
INSERT INTO val VALUES('myid8', 300);
INSERT INTO val VALUES('myid9', 240);
]]

-- user code
for i, tr in iselect(conn, "SELECT * FROM val;") do
    print( 'Row '..i..': ', tr.id, tr.num)
end
conn:close()

Conclusioni

Oltre a confermare l’intuitività e la semplicità degli iteratori in Lua, abbiamo dimostrato come SQLite3 e le librerie LuaJIT per accedervi, siano in grado di fornire una soluzione performante e potente per la creazione di applicativi non di rete.

Un saluto.

Iteratori in Lua


Costruzione di un iteratore in Lua

La costruzione di un iteratore in Lua si basa sulla creazione di una funzione che restituisce uno alla volta gli elementi dell’insieme nella sequenza desiderata. Una volta costruito l’iteratore, questo potrà essere impiegato in un ciclo foreach — che in Lua viene chiamato generic for — in modo molto efficiente.
Se per esempio si volesse iterare la collezione dei numeri pari compresi nell’intervallo da 1 a 10, avendo a disposizione l’apposito iteratore evenNum(first, last) che definiremo in seguito, potrei scrivere semplicemente:

for n in evenNum(1,10) do
   print(n)
end

ottenendo in console:

2
4
6
8
10

Numeri pari

Per definire questo iteratore dobbiamo creare una funzione che restituisce a sua volta una funzione in grado di generare la sequenza dei numeri pari. L’iterazione termina quando giunti all’ultimo elemento, la funzione restituirà il valore nullo ‘nil’, cosa che succede in automatico senza dover esplicitare un’istruzione di return.
Per prima cosa si calcola il numero pari successivo al numero di partenza dell’intervallo. Lo faremo usando la funzione math.ceil() che fornisce il numero arrotondato al primo intero superiore dell’argomento. Poi viene restituita la funzione in sintassi anonima che prima incrementa di 2 il numero pari precedente — ed ecco perché inizialmente viene sottratta la stessa quantità all’indice — e, se questo è inferiore all’estremo superiore dell’intervallo, verrà restituito l’indice corrente, il numero pari della sequenza:

-- iteratore dei numeri pari compresi
-- nell'intervallo [i, e]
function evenNum(i, e)
   -- primo numero pari della sequenza
   i = 2*math.ceil(i/2) - 2
   return function ()
             i = i + 2
             if i<=e then
                return i
             end
          end
end

Tecnicamente il sistema funziona perché ogni volta che viene chiamata una funzione, Lua crea le necessarie variabili in una nuova area di memoria, così che la funzione anonima possa lavorare correttamente. Ulteriori particolari possono essere reperiti nel libro ‘Programming in Lua‘, cosa consigliabile se si devono costruire iteratori in ambienti di produzione.

Naturalmente, l’implementazione data di evenNum() è solo una delle possibili soluzioni, e non è detto che non debbano essere considerate situazioni particolari come quella in cui si passa all’iteratore un solo numero o addirittura nessun argomento.

Stateless iterator

Una seconda versione del generatore di numeri pari può essere un buon esempio di un iteratore in Lua che non necessita di un area di memoria — chiamata closure — per un risultato ancora più efficiente.
Per capire come ciò sia possibile dobbiamo conoscere come funziona il ‘generic for’ in Lua; dopo la parola chiave ‘in’ esso si aspetta tre parametri: la funzione dell’iteratore da chiamare ad ogni ciclo, una variabile che rappresenta lo stato invariante, e la variabile di controllo.
Nel seguente codice la funzione evenNum() provvede a restituire i tre parametri necessari: la funzione nextEven() come iteratore, lo stato invariante che per noi è il numero a cui la sequenza dovrà fermarsi, e la variabile di controllo che è proprio il numero della sequenza dei numeri pari, e con ciò abbiamo realizzato un stateless iterator in Lua.
La funzione iteratrice nextEven() verrà chiamata ad ogni ciclo con nell’ordine lo stato invariante e la variabile di controllo, pertanto fate attenzione, dovete mettere in questo stesso ordine gli argomenti nella definizione.

-- even numbers stateless iterator
local function nextEven(last, i)
   i = i + 2
   if i<=last then
      return i
   end
end

local function evenNum(a, b)
   a = 2*math.ceil(a/2)
   return nextEven, b, a-2
end

-- example of generic for
for n in evenNum(10,20) do
print(n)
end

Lua non finisce mai di stupire…
Alla prossima!

L’Arte della pericolosità sismica – Parte 2


Sommario

Con un nuovo tocco di colore, torniamo sulla mappa della pericolosità sismica italiana per produrre un’altra pagina artistica. Questa volta useremo una scala di colori con quelli della nostra bandiera…

Colore italiano

Riprendendo il codice illustrato in questo post, è sufficiente creare una nuova funzione Lua, riportata in seguito, per ottenere la mappa sottostante:

La mappa di pericolosità sismica in versione tricolore

La mappa di pericolosità sismica in versione tricolore

Il colore, funzione dell’accelerazione, varierà da verde per il valore minimo a bianco per l’accelerazione media, fino ad arrivare al rosso per l’accelerazione massima.
In LaTeX, un colore può essere miscelato facilmente con il bianco specificandone dopo il nome ed un punto esclamativo, un valore variabile da 0 a 100. Il valore nullo corrisponde al colore bianco, mentre il valore 100 lascia intatto il colore.
Così, l’espressione ‘green!50’ corrisponde al colore bianco con il 50% di verde.

Il codice seguente mostra la funzione che genera l’espressione del colore in funzione dell’accelerazione. Valori bassi di pericolosità saranno rappresentati da un colore verde mescolato con sempre meno bianco a mano a mano che l’accelerazione decresce, mentre valori elevati saranno rappresentati da un colore rosso mescolato ad una componente decrescente di bianco:

-- funzione di interpolazione lineare
local function linear(x,x1,x2,y1,y2)
   return y1 + (y2-y1)*(x-x1)/(x2-x1)
end

-- funzione scala di colori dal verde al rosso
-- passando per il bianco
local function green2red(ag)
   local agmean = (agmax+agmin)/2
   local basecol, val
   if ag < agmean then
      basecol = 'green'
      val = linear(ag, agmin, agmean, 100, 0)
   else
      basecol = 'red'
      val = linear(ag, agmean, agmax, 0, 100)
   end
   
   return string.format('%s!%d', basecol, val)
end

Ho stampato anche questa nuova mappa, naturalmente a colori, e l’ho appesa in ufficio a beneficio della collezione primavera estate 2012.
Ciao.

L’Arte della pericolosità sismica


Sommario

Attraverso i software del mondo TeX, creeremo una visualizzazione artistica della mappa di pericolosità sismica italiana contenuta nella norma D.M. 14/01/2008. L’elaborazione grafica sarà opera del potente pacchetto TikZ progettato per LaTeX, mentre un piccolo programma in Lua si occuperà della costruzione logica.

Pericolosità sismica S1

Il progetto S1 di INGV, l’Istituto Nazionale di Geofisica e Vulcanologia, recepito poi dalla normativa tecnica, ha definito la pericolosità sismica nel territorio italiano attraverso una griglia di punti distanti uno dall’altro poco più di 5 km.
L’idea è quella di replicare le rappresentazioni grafiche diffuse dall’INGV con gli strumenti del mondo TeX, realizzando mappe a colori con i valori dell’accelerazione di picco al suolo PGA, Peak Ground Accelaration (un risultato è riportato in fondo all’articolo).

Immagine della mappa S1 tratta dal sito INGV

Immagine della mappa S1 tratta dal sito INGV

Struttura del codice

Ecco una descrizione di come verranno elaborati i dati:

  • uno script principale in Lua, efficiente linguaggio di scripting, leggerà i dati della pericolosità producendone un file di testo contenente comandi grafici per TikZ;
  • un sorgente LaTeX elaborerà i comandi producendo la mappa grafica in formato pdf.

I dati

Ho tradotto la tabella scaricabile dal sito del Consiglio dei Lavori Pubblici, contentente i dati di pericolosità sismica, in una forma direttamente comprensibile da Lua: ecco come appare il contenuto del file di dati che ho chiamato ‘GridS1.lua’:

GridS1 = {
[13111] = {lon=6.5448, lat=45.134, Tr30={0.0263, 2.50, 0.18}, Tr50={0.0340, 2.51, 0.21}, ...ecc
[13333] = {lon=6.5506, lat=45.085, Tr30={0.0264, 2.49, 0.18}, Tr50={0.0341, 2.51, 0.21}, ...ecc
[13555] = {lon=6.5564, lat=45.035, Tr30={0.0264, 2.50, 0.18}, Tr50={0.0340, 2.51, 0.20}, ...ecc
...ecc

Creare i comandi grafici

Disegneremo per ciascun punto della griglia S1, un quadratino colorato secondo una data scala di colori associata al valore della PGA, ma per farlo dobbiamo risolvere due problemi:

  • tradurre la coppia longitudine, latitudine dei punti in coordinate nel piano x, y;
  • associare un colore al valore di accelerazione.

Il sorgente Lua mostrato nel seguito dapprima carica in memoria i dati strutturati della griglia, determina i valori minimi e massini dell’accelerazione tra tutti i 10751 punti (per il tempo di ritorno di 475 anni) utili in seguito, poi definisce le due funzioni fondamentali di trasformazione delle coordinate (trasf()) e di associazione valore->colore (wavecolor).

Preparato il terreno, lo script si conclude con la funzione go(), che scrive su un file esterno chiamato defpoint.tex, le istruzioni TikZ di disegno.

Notate che la funzione go() accetta come argomenti anche delle funzioni. Il vantaggio immediato di questo tipo di programmazione disponibile in Lua è che la funzione go() diviene indipendente sia dal tipo di trasformazione di coordinate che dalla scala di colore (io ho scelto di lavorare con la definizione di colore basato sulla lunghezza d’onda della luce con rapporto lineare rispetto alla PGA).

Per i più curiosi, nei commenti del codice sono riportati i due punti della maglia S1 corrispondenti al punto sismicamente più pericoloso e quello meno pericoloso sul territorio della penisola italiana…

-- carica la tabella dati dal file GridS1.lua
dofile "GridS1.lua"

-- funzione di massimo o minimo valore
local function S1(funval, key)
   local rif = funval(GridS1[13111])
   local id

   for idx, t in pairs(GridS1) do
      local val = funval(t)
      if key == "max" then
         if  val > rif then
            rif = val
            id = idx
         end
      elseif key == "min" then
         if val < rif then
             rif = val
             id = idx
          end
       else
          error("The key '"..key"' is not supported.")
       end
    end
    return rif, id
end
-- funzioni di estrazione dati singolo nodo
local function Lat(t)
    return t.lat
end
local function Lon(t)
    return t.lon
end
local function Ag475(t)
    return t.Tr475[1]
end
local function countNode()
    local i = 0
    for _, t in pairs(GridS1) do
       i = i + 1
    end
    return i
end
-- valori minimi e massimi dei dati di mappa
local agmax, idmax = S1(Ag475, 'max')  --> 0.278 , 49640
local agmin, idmin = S1(Ag475, 'min')  --> 0.0364, 12693

-- latitudine
local latmax = S1(Lat, 'max')   --> 47.178
local latmin = S1(Lat, 'min')   --> 36.573

-- longitudine
local lonmax = S1(Lon, 'max')   --> 18.594
local lonmin = S1(Lon, 'min')   --> 6.5448

-- numero nodi S1 (solo per saperlo...)
local numnode = countNode()     --> 10751

-- funzione di servizio
local function makecmdfile(fn, t)
   local outputfile = assert(io.open(fn..".tex","w"))
   outputfile:write(table.concat(t,"\n"))
   outputfile:close()
end

-- restituisce le coordinate di mappa
-- da quelle geografiche
local function trasf(lon, lat)
   -- fattore di conversione gradi->cm
   local f = 4.1

   return 0.80*lon*f, lat*f
end

-- funzione scala di colori wavelets
local function wavecolor(ag)
   local w1 = 363
   local w2 = 814
   local m = 80
   w1 = w1 + m
   w2 = w2 - m
   return w1 + (w2-w1)*(ag-agmin)/(agmax-agmin)
end

-- maschera di formato disegno
local cmask = '\\convertcolorspec{wave}{%d}{rgb}\\col\\color[rgb]{\\col}'
local dmask = '\\draw (%0.3f,%0.3f) node {};'

-- filename = nome senza estensione del file risultato
-- fcoord = funzione di trasformazione coordinate
-- fcolor = funzione di trasformazione colore
local function go(filename, fcoord, fcolor)
   -- memorizzazione comandi
   local t = {}
   for id, dati in pairs(GridS1) do
      local ag = Ag475(dati)
      local lt = Lat(dati)
      local ln = Lon(dati)

      t[#t+1] = string.format(cmask, fcolor(ag))
      t[#t+1] = string.format(dmask, fcoord(ln,lt))
   end

   makecmdfile(filename, t)
end

-- esecuzione
go('defpoint', trasf, wavecolor)

Creare la mappa

Per creare la mappa è sufficiente compilare con pdfLaTeX questo minuscolo file:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{xcolor}
\tikzstyle{every node}=[
    fill,
    minimum size=0.1pt
]

\begin{document}
\begin{tikzpicture}
\input{defpoint}
\end{tikzpicture}
\end{document}

Risultato

La mappa risultato è questa, insieme di 10751 quadratini colorati in modo diverso uno dall’altro (fate un click sull’immagine per scaricarla, un’esperienza da brivido…):

Mappa della pericolosità sismica italiana

La mappa della pericolosità sismica italiana risultato dell’elaborazione descritta nell’articolo

Note tecniche

Per riprodurre la mappa eseguendo il codice mostrato nel post, occorre disporre di una installazione TeX, come TeX Live o MiKTeX, e l’interprete Lua, tutti strumenti gratuiti, o meglio, liberi.
Avremo potuto lavorare con un unico strumento (Lua incorporata in TeX, ovvero LuaTeX, ma lascio il facile esercizio al lettore che non perderà l’occasione di inventarsi nuove scale di colore… 🙂
Alla prossima!

Come mandare Lua in crash


I software sono perfettibili, ed anche Lua, il bellissimo linguaggio di scripting, non è da meno. Guardate infatti cosa succede se si apre una sessione di Lua, digitando il comando lua, e dando l’istruzione seguente:

print(os.date('%G'))

Il formato ‘%G’ infatti non fa parte dei codici accettati per formattare la data di sistema ma… invece di ricevere il classico errore…

The Lua console open on a dangerous istruction...

The Lua console open on a dangerous istruction…

Lua crash Dialog on Windows after command execution

Lua crash Dialog on Windows after command execution


Dalle immagini potete riscontrare che mi trovo in Windows con Lua 5.1.
Buona estate, che finalmente è arrivata!
R.

Click and script


Scarica l’articolo nel formato PDF

Sommario

In questo post impareremo come personalizzare il menù contestuale in Nautilus, il gestore di file incorporato in Gnome, fino a compilare al volo sorgenti LaTeX e compiere moltissime altre operazioni che possono essere ripetitive.

Il menù contestuale

Nelle moderne interfacce grafiche il menù contestuale è un efficace idea che presenta all’utente le operazioni effettuabili su uno specifico oggetto. Se per esempio si tratta di un file compresso il click destro sull’icona che lo simboleggia, attiverà un menù contestuale che presenta le apposite voci per l’estrazione del contenuto.

In Linux, nel Desktop Environment Gnome è possibile associare a questi menù piccoli programmi per lanciarli comodamente dopo aver selezionato i file d’interesse.

Nautilus-scripts

Nautilus è il file manager di Gnome. Grazie al supporto per lo scripting, le operazioni per creare una nuova voce di menù contestuale sono davvero semplici, allo stesso modo di quelle da svolgere per creare un nuovo modello di documento come già illustrato in questo post. Basta creare il file con il codice, assegnargli i permessi di esecuzione e spostarlo nella directory nascosta $HOME/.gnome2/nautilus-scripts.

Menù contestuale su un file compresso in Gnome

Menù contestuale su un file compresso in Gnome

Come è naturale sui sistemi Linux, possiamo scrivere script in molti linguaggi, da Bash a Python. Noi utilizzeremo anche Lua pertanto dovrete assicurarvi che sia installato sul vostro sistema.

I nostri comandi personalizzati

Ecco in concreto, alcune idee di personalizzazione del menù. Ciascun nuovo comando contestuale deriva da esigenze della pratica quotidiana.
Molte altre idee possono essere realizzate per rendere più semplice l’utilizzo del pc, per esempio unire più file di testo o spedire documenti ad un indirizzo di posta elettronica…

Mandare in stampa un documento

Se desidero lanciare la stampa di un file PDF lo apro con un visualizzatore ed eseguo il comando di stampa. Se i PDF da stampare sono molti invece, apro il terminale, posiziono la working directory e lancio la stampa di tutti i PDF presenti con il comando:

$ lpr *.pdf

Con qualche conoscenza in più di Bash (il linguaggio standard per l’amministrazione dei sistemi Linux ma anche di quelli Mac OS X ormai), si costruisce un piccolo e semplicissimo script:

#!/bin/bash

for arg do
    lpr "$arg"
done

Salviamone il contenuto in un file di testo chiamato per esempio goToPrinter, assegniamogli i permessi di esecuzione (anche dal menù Proprietà) e spostiamolo nella directory nascosta $HOME/.gnome2/nautilus-scripts. Di seguito la sequenza dei comandi e l’immagine della sessione corrispondente al terminale:

$ chmod +x goToPrinter
$ mv goToPrinter ~/.gnome2/nautilus-scripts
Sessione al terminale per la creazione del Nautilus script

Sessione al terminale per la creazione del Nautilus script

Elencare file

Modificando un poco lo script precedente possiamo ottenere facilmente un file di testo contenente la lista dei file selezionati sostituendo ad lpr il comando echo e reindirizzandone l’output verso un file. Utilizzeremo poi la libreria zenity per mostrare un dialogo che mostri all’utente il numero dei file conteggiati.

#!/bin/bash

for arg do
    echo "$arg"
done >> listoffiles.txt

zenity --info --width=200 --height=100 \
       --title="Conteggio file elencati" \
       --text="Numero di file elencati: $# "

Compilare un sorgente LaTeX

Compilare un sorgente LaTeX non è un problema se ci si avvale di un editor come TeX Works, oppure direttamente del terminale, ma se lo possiamo fare con un semplice click destro sulla selezione di file è davvero elegante.
Il codice in Lua provvede a filtrare i file selezionati al momento dell’apertura del menù contestuale in base all’estensione solitamente assegnata ai sorgenti LaTeX.

#!/usr/bin/lua

-- ottengo la lista dei file selezionati
-- dalla variabile d'ambiente opportuna
--
local s = os.getenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS")

-- per ogni file con estensione .tex
-- procedo alla compilazione
--
for path in string.gmatch(s,"(.-)\n") do
   -- estrazione estensione
   local ext = string.match(path,"%.(.-)$")
   
   -- se l'estensione esiste allora compilo
   -- se è un sorgente .tex
   if ext then
   if string.lower(ext) == "tex" then
      local ps = "(.-)%." .. ext .. "$"
      local name = string.match(path,ps)
      
      os.execute('pdflatex -interaction=nonstopmode "'..name..'"')
      os.remove(name..".log")
      os.remove(name..".aux")
   end
   end
end

-- end of Lua script

Ottenere il pdf di file di testo

Con poche modifiche possiamo adattare il codice precedente per ottenere uno script che converte i file di testo in formato PDF utilizzando LaTeX:

#!/usr/bin/lua

local docsource = [[
\documentclass[a4paper]{article}

\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[italian]{babel}

\usepackage[margin=20mm]{geometry}

\usepackage{inconsolata}

\begin{document}
\ttfamily
%s
\end{document}
]]

-- ottengo la lista dei file
local s = os.getenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS")

for path in string.gmatch(s,"(.-)\n") do
   -- estrazione estensione
   local ext = string.match(path,"%.(.-)$")
   
   -- se l'estensione esiste allora creo il pdf con LaTeX
   if ext then
   if string.lower(ext) == "txt" then
      local ps = "(.-)%." .. ext .. "$"
      local filename = string.match(path,ps)
      
      -- lettura del file
      local textfile = io.open(path,"r")
      local content = string.gsub(textfile:read("*all"),"\n","\n\n")
      local sourcetexfile = string.format(docsource,content)
      textfile:close()
      
      -- creazione sorgente
      local sf = io.open(filename..".tex","w")
      sf:write(sourcetexfile)
      sf:close()
      
      os.execute("pdflatex -interaction=nonstopmode "..filename)
      os.remove(filename..".log")
      os.remove(filename..".aux")
      os.remove(filename..".tex")
   end
   end
end

-- end of Lua script

Conclusioni

Il menù contestuale è davvero comodo. In Linux possiamo utilizzare diversi linguaggi di scripting per creare nuove voci di comandi con cui semplificare il lavoro quotidiano analizzando attentamente le nostre necessità.
Un saluto.

Note tecniche

Sul sistema devono essere installati e correttamente configurati i pacchetti e gli eseguibili di un sistema TeX ed un interprete Lua.
Gli esempi potrebbero richiedere leggere modifiche per distribuzioni Linux diverse da Ubuntu 10.04.

Il computo metrico con Lua


Scarica l’articolo in formato PDF per la stampa

Sommario

Certi tipi di opere edili possono essere così articolate da mettere in difficoltà l’utente che intendesse gestirle con un tradizionale software di computazione.
In questo post si svilupperà un’idea che può risolvere il problema di efficienza, secondo la quale i dati sono descritti da un opportuno linguaggio così da esprimere in modo diretto e concettuale le particolarità dell’opera edilizia.

Computi metrici

Un computo metrico di un opera consiste in una serie di voci che definiscono i singoli componenti edilizi quantificate secondo una specifica unità di misura. Inserire le misure è una di quelle attività leggermente noiose e poco attraenti…

Con un linguaggio specifico è possibile rendere efficiente l’inserimento manuale di queste quantità, in particolare se si desidera che i dati siano raggruppati in livelli strutturati.

Il linguaggio dei solai

Se l’opera in progetto è composta da diversi edifici a più piani, dovremo calcolare le superfici dei solai raggruppando i dati secondo la doppia classificazione piano/edificio. Saremo così in grado di fornire le superfici di solaio per ciascun piano, quelle totali per singolo edificio e quelle complessive per piano.

Un linguaggio semplice che descrive questa struttura informativa potrebbe essere questo:

solaio{
   edificio = "A",
   piano = 1,
   superficie = 4.50*3.25,
}

La sintassi che riscontriamo leggendo il frammento di codice, si basa su un costrutto chiave = valore ciascuno separato con una virgola e racchiuso in un coppia di parentesi graffe.

I valori testo sono racchiusi tra doppi apici mentre i valori numerici utilizzano il punto e non la virgola per separare la parte decimale.
Vi anticipo che il codice precedente corrisponde alla sintassi di Lua, un linguaggio di programmazione libero e multi-piattaforma, più volte trattato sul blog.

Continuando a tagliare su misura il codice per renderlo perfettamente adatto ad esprimere le quantità che vorremmo computare, osserviamo che non ci occorre identificare i singoli solai di uno stesso piano di uno stesso edificio. Alla chiave superficie assegniamo allora non un singolo valore o espressione, ma un elenco di espressioni corrispondenti alla geometria dell’impalcato, per esempio (possiamo commentare il codice inserendo due trattini):

-- cambiamo nome all'oggetto 🙂
solaiDiPiano{
   edificio   = "A",
   piano      = 1,
   superficie = {
      2*4.50*3.25,           -- qui due solai uguali
      3.80*3.25,
      4.64*4.20 - 0.40*0.70, -- qui una detrazione di una botola!
      4.24*(4.20 + 3.50),
      5.50*3.60,             -- la virgola finale è opzionale
      },                     -- ma rende più comode successive
}                            -- aggiunte di valori

Osservazioni operative

L’ambiente in cui si memorizza il codice è un editor di testo ASCII. La comodità sta nel fatto che i dati sono immediatamente disponibili su qualsiasi sistema operativo per eventuali modifiche o verifiche, e possiamo eseguire le operazioni con poca fatica.

Nel frattempo ho aggiunto altri dati come potete vedere dal prossimo frammento di codice:

solaiDiPiano{edificio="A", piano=1,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="A", piano=2,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="A", piano=3,
   superficie = {
      2*(1.65+3.60+2.12)*(4.42+4.32),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="B", piano=1,
   superficie = {
      2*(2.54+2.54+2.56+2.545)*(4.345+4.245),
      2*1.00*2.64
   }
}

solaiDiPiano{edificio="B", piano=2,
   superficie = {
      2*(2.54+2.54+2.56+2.545)*(4.345+4.245),
      2*1.00*2.64
   }
}

solaiDiPiano{edificio="C", piano=1,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="C", piano=2,
   superficie = {
      2*(1.60+3.60+2.07)*(4.345+4.245),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

solaiDiPiano{edificio="C", piano=3,
   superficie = {
      2*(1.65+3.60+2.12)*(4.42+4.32),
      1.92*2.64,
      3.60*(0.55+1.05)*2,
   }
}

Elaborare i dati

Ma come si elaborano i dati?
Il codice scritto corrisponde esattamente ad una serie di chiamate alla funzione Lua solaiDiPiano() a cui passiamo una tabella.
Eseguendo il codice, verranno calcolate le espressioni delle superfici e le istruzioni contenute nella funzione.

Elaborazione diretta

Proviamo a scrivere la funzione in modo da ottenere la superficie totale dei solai:

-- superficie totale
local totsup = 0

-- definizione della funzione
function solaiDiPiano(s)
   -- campi in s:
   -- s.superficie -> array di valori

   -- sommo tutti i valori in s.superficie
   -- e li inserisco nel totale
   for _, val in ipairs(s.superficie) do
      totsup = totsup + val
   end
end

-- carico il file dati
dofile("solai.lua")

-- stampo il risultato
print(string.format("Superficie totale : %0.3f",totsup))

Una volta installato sul sistema Lua, inseriamo i dati dei solai così come si è deciso di descriverli nel file testo “solai.lua”. Salviamo in un secondo file il codice che li elabora “calcsuptot.lua”, ed infine apriamo il terminale o la console ed eseguiamo il comando:

> lua calcsuptot.lua

Fatto! La schermata rappresentativa dell’operazione in Windows è la seguente, dove si può notare l’uso del comando cd per spostarsi nella directory dove avevo salvato i due file.

Esecuzione del report per la superficie totale in console

Esecuzione del report per la superficie totale in console

Elaborazione strutturata

Diamo una definizione strutturata della funzione solaiDiPiano() in modo da costruire una tabella dati chiamata Solai che potremo leggere in modi diversi. Questa è la via sicuramente più flessibile e potente.

-- serve un contenitore per i dati:
local Solai = {}

-- creazione della tabella dati
function solaiDiPiano(s)
   -- ricordiamo i nomi dei campi in s
   -- s.edificio   := nome edificio
   -- s.piano      := numero di piano
   -- s.superficie := array di valori

   -- se non esiste ancora creiamo
   -- una tabella per edificio
   -- rispetto al nome
   -- es.: Solai.A
   if not Solai[s.edificio] then
     Solai[s.edificio] = {}
   end

   -- prendiamoci un riferimento più comodo
   -- alla singola tabella edificio
   local edificio = Solai[s.edificio]

   -- calcoliamo la somma delle superfici
   local suppiano = 0
   for _, val in ipairs(s.superficie) do
      suppiano = suppiano + val
   end

   -- intenderemo la tabella edificio
   -- come un array dove l'indice sarà
   -- il numero di piano ed il valore
   -- corrispondente, la superficie
   if edificio[s.piano] then
      edificio[s.piano] = edificio[s.piano] + suppiano
   else
     edificio[s.piano] = suppiano
   end
end

Fatto questo, potremo elaborare decine od anche migliaia di elementi si solaio. Tutti i dati saranno memorizzati nella tabella Solaio Solaio.

A questo punto scriviamo la funzione di reporting che opererà in lettura. Vogliamo stampare per esempio i totali di piano per edificio:

-- report piano/edificio
function reportPianoEdificio()
   -- Solai contiene i dati in modo strutturato
   -- Al primo livello ci sono le tabelle edificio

   local totsup = 0
   for nomeEd, arraysup in pairs(Solai) do
      print(string.format("Edificio %s: --------------------", nomeEd))

      -- stampiamo le superfici piano per piano
      local totsupEd = 0
      for level, sup in pairs(arraysup) do
         totsupEd = totsupEd + sup
         print(string.format("Piano %2d, superficie %10.3f", level, sup))
      end

      totsup = totsup + totsupEd
      print("--------------------------------")
      print(string.format("Tot. sup. edificio %s: %10.3f", nomeEd, totsupEd))
      print("--------------------------------")
   end
   print("--------------------------------")
   print(string.format("Tot. superfici %10.3f", totsup))
   print("--------------------------------")
end

In console ottengo questo:

Esecuzione del report in console

Esecuzione del report Piano/Edificio in console

Se volessimo stampare le superfici piano per piano dovremo costruire una funzione in lettura di questo tipo:

-- report per piani
function reportPiani()
   -- una tabella per le sup di piano
   local suppiani = {}

   -- ciclo per singolo edificio
   -- primo livello della struttura dati Solaio
   for nomeEd, arraySup in pairs(Solai) do
      -- ciclo interno di lettura delle sup
      for piano, sup in pairs(arraySup) do
         if suppiani[piano] then
            suppiani[piano] = suppiani[piano] + sup
         else
            suppiani[piano] = sup
         end
      end
   end

   -- stampa
   local ts = 0
   for level, sup in pairs(suppiani) do
      ts = ts + sup
      print(string.format("Livello %2d: sup = %10.3f",level,sup))
   end
   print("--------------------------")
   print(string.format("Totale sup = %10.3f",ts))
end

--[[
-- l'output con i soliti dati è questo
Livello  1: sup =    463.233
Livello  2: sup =    463.233
Livello  3: sup =    290.833
--------------------------
Totale sup =   1217.299
--]]

Conclusioni

L’idea proposta è originale? No! Volendo, si possono fare molti esempi tratti dal mondo dell’informatica dove un linguaggio testuale descrive dati strutturati. Con Lua è semplice creare il proprio linguaggio ed elaborare i dati, pensiamo per esempio al caso più complesso riguardante la costruzione dei libretti dei ferri…

Naturalmente, potremo preparare report molto più professionali che non un semplice output in console, per esempio compilando un opportuno sorgente LaTeX per l’uscita in PDF, direttamente con le funzioni Lua di reporting.

Uno svantaggio consiste nella necessità di scrivere qualche linea di codice Lua, ma del resto questo consente la massima libertà di cucire la soluzione sul problema e non è poi così terribilmente difficile. A voi dunque l’onore e l’onere di rendere un po’ più attraente la stesura dei computi metrici!

Note tecniche

Il software occorrente per utilizzare la procedura è il seguente: un editor di testi, consiglio SciTE perché in grado di far eseguire il codice premendo semplicemente il tasto F5, e l’interprete Lua, quindi tutto software libero, gratuito e legalmente installabile anche in ambito professionale, infine un pizzico di fantasia.
Per imparare Lua non vi è niente di meglio del PiL.
Un saluto.

Gli oggetti di Lua


Click to download the article in the PDF file format

Sommario

Il linguaggio Lua è fondato sull’essenzialità tanto che supporta la programmazione ad oggetti utilizzando quasi esclusivamente le risorse di base e senza mettere a disposizione specifici costrutti. In questa introduzione vedremo come ciò sia stato effettivamente implementato utilizzando l’unica struttura dati disponibile in Lua: la tabella.

Per la comprensione del testo occorre una conoscenza di base sia di Lua, sia dei concetti del paradigma di programmazione ad oggetti, utile per introdurre nei programmi un più elevato livello d’astrazione.

Un rettangolo

Come esempio costruiremo un oggetto per rappresentare un rettangolo, un ente geometrico definibile con due parametri, le lunghezze dei lati, e dotato di una proprietà calcolabile chiamata area. Gli elementi semplici che memorizzano i valori dei lati sono detti campi mentre la proprietà dell’area che ne dipende è detta metodo.

Gli oggetti in Lua si rappresentano con le tabelle che possono contenere valori come numeri ed anche funzioni. Un primo tentativo potrebbe essere questo (sperimentatelo in modo interattivo al terminale per rendervi meglio conto del codice):

-- prima implementazione
Rettangolo = {} -- creazione tabella (oggetto)

-- creazione di due campi
Rettangolo.a = 12
Rettangolo.b =  7

-- un metodo
function Rettangolo.area ()
   -- accesso alla variabile 'Rettangolo'
   return Rettangolo.a * Rettangolo.b
end

-- primo test
print(Rettangolo.area()) --> stampa 84, OK
print(Rettangolo.a)      --> stampa 12, OK

Ci accorgiamo presto che questa implementazione basata sulle tabelle è difettosa in quanto non rispetta l’indipendenza degli oggetti rispetto al loro nome, ed infatti il prossimo test fallisce:

-- ancora la prima implementazione
Rettangolo = {a=12, b=7}

-- un metodo
function Rettangolo.area ()
   -- accesso alla variabile 'Rettangolo'
   return Rettangolo.a * Rettangolo.b
end

-- secondo test
r = Rettangolo   -- creiamo un secondo riferimento
Rettangolo = nil -- distruggiamo il riferimento originale

print(r.a)               --> stampa 12, OK
print(r.area())          --> errore!

Il problema sta nel fatto che nel metodo area() compare un particolare riferimento che invece deve poter essere qualunque. La soluzione è introdurre il riferimento all’oggetto come parametro esplicito nel metodo stesso, che chiameremo self, così da poter generalizzarne la validità. Questa idea è quella utilizzata dai linguaggi di programmazione che supportano gli oggetti.

Secondo questo nuovo schema, dovremo riscrivere il metodo area() in questo modo:

-- seconda implementazione
Rettangolo = {a=12, b=7}

-- il metodo diviene indipendente dal particolare
-- riferimento all'oggetto:
function Rettangolo.area ( self )
   return self.a * self.b
end

-- ed ora il test
myrect = Rettangolo
Rettangolo = nil -- distruggiamo il riferimento

print(myrect.a)            --> stampa 12, OK
print(myrect.area(myrect)) --> stampa 84, OK

Fino ad ora abbiamo costruito l’oggetto sfruttando le caratteristiche della tabella e la particolarità che consente di assegnare una funzione ad una variabile, ma da questo momento entra in scena l’operatore due punti (:), nella chiamata di funzione permettendo di passare il riferimento implicitamente.

Questo operatore è il primo nuovo elemento di Lua inteso per supportare la programmazione orientata agli oggetti, ed eccone una descrizione:

Se si accede ad un metodo memorizzato in una tabella con l’operatore due punti ‘:‘ anziché con l’operatore ‘.‘, sarà aggiunto implicitamente un primo parametro con il riferimento alla tabella stessa chiamato self.

Le seguenti due espressioni sono perfettamente equivalenti per risultato pratico, ma hanno due differenti punti di vista concettuali:

-- operatore punto
-- dobbiamo inserire il riferimento
-- come primo parametro della funzione
print(myrec.area(myrec)) --> stampa 84, OK

-- operatore due punti
-- il riferimento 'myrec' viene
-- passato implicitamente
print(myrec:area())      --> stampa 84, OK

Prima classe

Il salto definitivo nella programmazione OOP (Object Oriented Programming) è la costruzione di una classe. Dovremo infatti poter costruire nuovi oggetti senza ogni volta assemblare i campi ed i metodi, insomma serve un qualcosa che faccia da stampo.

Per mantenere la filosofia di essenzialità, Lua non implementa, come invece troviamo in altri linguaggi simili, per esempio Python, una nuova parola chiave class con cui si definisce un prototipo ma, ancora una volta, propone una soluzione basata sulle tabelle, anzi sulle metatabelle.

Metatabelle

Una metatabella è una tabella che contiene funzioni dai nomi prestabiliti che vengono eseguiti quando si verificano particolari eventi, come la richiesta di somma tra due tabelle. Ogni tabella può essere associata ad una propria metatabella e questo consente di creare degli insiemi di tabelle che condividono una stessa aritmetica, giusto per rimanere in nell’esempio precedente.

I nomi di queste funzioni particolari iniziano tutti con un doppio trattino basso, per esempio nel caso della somma sarà richiesta la funzione __add() della metatabella associata, e vengono chiamati metametodi.

Il metametodo più semplice di tutti è __tostring(), che viene chiamato quando alla funzione print() viene data una tabella. In una sessione di terminale scriviamo:

-- un numero complesso
complex = {real = 4, imag=-9}
print(complex) --> stampa: 'table: 0x9eb65a8'

-- un qualcosa di più utile:
function printComplex( c )
   local r = string.format("%0.2f",c.real)
   if c.imag == 0 then -- numero reale
      return "("..r..")"
   end
   -- numero complesso
   local i = string.format("%0.2f",c.imag)
   return "("..r..","..i..")"
end

-- creo la metatabella
mt = {}
mt.__tostring = printComplex

setmetatable(complex, mt)

-- riprovo la stampa
print(complex)  --> stampa '(4.00,-9.00)'

Il metametodo __index()

Il ‘metametodo’ che interessa per la OOP in Lua è __index(). Esso interviene quando chiamiamo un componente di una tabella che non esiste e che normalmente restituirebbe il valore nil.

Lua in sessione interattiva al terminale di Ubuntu Linux

Lua in sessione interattiva al terminale di Ubuntu Linux

Ecco un esempio di codice (nell’immagine la relativa sessione al terminale):

-- una tabella con un campo 'a'
t = {a = 'Campo A'}

print(t.a)  --> stampa 'Campo A'
print(t.b)  --> stampa 'nil'

Costruiamo invece la funzione __index() e vediamo che succede:

t = {a='Campo A'}

mt = {} -- la metatabella

-- creazione del metametodo
function idx()
  return 'Attenzione: campo inesistente!'
end

-- inseriamolo nella tabella mt
mt.__index = idx

-- assegnamo 'mt' come metatabella di 't'
setmetatable(t, mt)

-- adesso riproviamo ad accedere al metodo b
print(t.b)  --> stampa 'Attenzione: campo inesistente!'

Di nuovo un rettangolo

Torniamo adesso all’oggetto Rettangolo e creiamo una tabella che fungerà da classe per gli oggetti, assegnando campi e metodi. Per creare nuovi oggetti, creeremo un metodo new() che esaudirà la richiesta assumendo il ruolo di costruttore.

-- il nuovo oggetto Rettangolo

-- campi
Rettangolo = {a=10, b=10}

-- metodo
function Rettangolo:area()
  return self.a * self.b
end

-- creazione metametodo
Rettangolo:__index = Rettangolo

-- il costruttore
function Rettangolo:new( o )
   -- creazione nuova tabella
   -- se non ne viene fornita una
   o = o or {}

   -- assegnazione metatabella
   setmetatable(o, self)

   -- restituzione riferimento oggetto
   return o
end

-- test ---------------------------
-- rettangolo 10 x 10
r1 = Rettangolo:new()

print(r1.a)      --> stampa   10 OK
print(r1:area()) --> stampa  100 OK

-- rettangolo 200 x 10
r2 = Rettangolo:new{a=200}
print(r2.a)      --> stampa  200 OK
print(r2:area()) --> stampa 2000 OK

-- rettangolo 12 x 7
r3 = Rettangolo:new{a=12,b=7}
print(r3:area()) --> stampa   84 OK

Come funziona?

Il ‘costruttore’ accetta una tabella che gli viene passata dall’utente, altrimenti ne crea una vuota e la restituisce non appena ne abbia assegnato la metatabella che non è altro che l’oggetto Rettangolo stesso visto che l’operatore due punti passa a new il riferimento implicito a Rettangolo, per cui self punta a Rettangolo.

Nel primo caso del test, viene chiamato il campo a dell’oggetto r1 che non esiste, allora Lua, poiché invece esiste una metatabella associata ad r1, ne chiama il metodo __index, che restituisce semplicemente la tabella Rettangolo stessa. Alla fine viene restituito il campo a di Rettangolo che vale 10.

Stessa cosa succede quando viene chiamata la funzione area con la tecnica dell’operatore due punti, ovvero la funzione chiamata è quella del corrispondente campo in Rettangolo poiché non esiste un campo simile in r1.

Analoghe spiegazioni valgono quando al costruttore viene passata una tabella con un unico campo oppure con tutti e due i campi a e b. Questa volta l’oggetto possiede effettivamente campi propri per i lati ed il metametodo __index non viene considerato.

Questa volta un cerchio

Costruiamo un oggetto Cerchio che consenta di aggiungere una quantità al raggio. Ci renderemo meglio conto di come funziona il meccanismo nascosto ed automatico delle metatabelle:

Cerchio = {radius=0}
Cerchio.__index = Cerchio

function Cerchio:area()
   return math.pi*self.radius^2
end

function Cerchio:addToRadius(v)
   self.radius = self.radius + v
end

function Cerchio:__tostring()
   local frmt = 'Io sono un cerchio di raggio %0.2f.'
   return string.format(frmt, self.radius)
end

function Cerchio:new(r)
   -- il costruttore attende l'eventuale
   -- valore del raggio del cerchio
   local o = {}

   if r then
      o.radius=r
   end

   setmetatable(o, self)
   return o
end

-- test ----------------------
o = Cerchio:new()
print(o)

o:addToRadius(12.345)

print(o)
print(o:area())

Come funziona?

Vorrei soffermarmi su quanto accade nel codice di test. Abbiamo creato un cerchio senza fornire alcun valore per il raggio. Ciò significa che quando lo stampiamo con la successiva istruzione, il raggio è quello dell’oggetto Cerchio, configurato al valore di default nullo, per effetto della chiamata ad __index della metatabella.

Fino a questo momento la tabella dell’oggetto o non contiene alcun campo radius. Cosa succede allora quando viene lanciato il comando o:addToRadius(12.345)?

Il metodo addToRadius() contiene una sola espressione. Come da regola viene prima valutata la parte a destra self.radius + v, dunque il primo termine assume il valore previsto in Cerchio grazie al metametodo, ed alla fine viene creato anche per l’oggetto o il campo radius.

Conclusioni

Non tratteremo in questo post il meccanismo peraltro semplice dell’ereditarietà degli oggetti in Lua. Ciò detto possiamo renderci conto che in Lua si possono implementare strutture con il paradigma della OOP. Lo scopo è quello di fornire all’utente funzionalità più facili da utilizzare e contemporaneamente razionalizzare il codice che dovrebbe essere più facile da gestire nel tempo.

Una volta compreso il meccanismo basato sulle metatabelle, possiamo ideare facilmente e con una certa flessibilità classi di oggetti, ponendo l’attenzione sui metodi e sui campi. In ultimo vi faccio osservare che in Lua non è previsto nessun meccanismo da parte del linguaggio per rendere privati metodi e campi. Scelta coerente per un linguaggio di scripting che mira ad essere essenziale, quindi non ci rimane semplicemente non accedere ai campi, e sarà mantenuta la privacy degli oggetti.

Note tecniche

Per le note tecniche sottolineo che Lua è un software libero multi-piattaforma pertanto è liberamente installabile su sistemi meno diffusi e, ovviamente, anche in Windows, Linux e Mac.

Per sapere qualcosa di più sulle tabelle potete consultare i post di questo stesso Blog, in particolare questo dove trovate un introduzione alle tabelle di Lua.

Per la comprensione dei meccanismi di Lua per la OOP è caldamente consigliato ripetere gli esempi proposti, eventualmente utilizzando la modalità interattiva al terminale (è sufficiente digitare alla linea di comando lua per entrarvi) o lavorando con i file, proponendo altri esempi interessanti (postateli nei commenti mi raccomando…).
Happy Lua!

Aprire finestre sul PDF


Sommario

Lavoreremo con i linguaggi Lua e LaTeX per estrarre finestre da un qualsiasi file in formato PDF.
Capita infatti di voler riportare in un nostro documento una tabella, un grafico, un immagine o qualsiasi altro materiale contenuto in un secondo file PDF.
In questo post impareremo ad estrarre rettangoli qualsiasi da una qualsiasi pagina PDF, e l’utilità sta nel fatto che possiamo ottenere le “finestre” nello stesso formato pdf mantenendo la qualità vettoriale dell’originale.

Finestre

Vogliamo ritagliare una finestra rettangolare da un file pdf (che chiameremo originale.pdf). Per esempio, a pagina 21 è riportata una tabella che vogliamo inserire in una presentazione o in un nostro articolo senza perdere in qualità rispetto all’originale.

L’idea per estrarre il contenuto così definito, si basa sulle abilità di manipolazione dei file pdf del sistema TeX che, grazie al pacchetto graphicx, è in grado di ritagliare rispetto alla pagina la finestra voluta.
Dobbiamo quindi avere a disposizione il file pdf contenente quell’unica pagina in cui si trova il materiale da ritagliare e per far questo, ancora una volta ci rivolgeremo ad un pacchetto LaTeX chiamato pdfpages.

L’idea di estrarre la particolare pagina e ritagliarla successivamente, comporta quindi la scrittura e la compilazione di due file sorgenti LaTeX. Ed ecco che entra in scena Lua che ci facilita questo compito.

Estrarre una pagina

Assumendo i dati espressi precedentemente, lo script Lua seguente crea un file .tex opportuno, lo compila con il programma pdflatex e cancella i file temporanei lasciando nella cartella, accanto al file pdf originale (che viene soltanto utilizzato in lettura), il file tmppage21.pdf.
Da notare l’uso del delimitatore di stringa delle doppie parentesi quadre e l’uso della funzione string.format() della libreria standard di Lua, con cui creiamo il sorgente attraverso gli opportuni segnaposto (il numero della pagina ed il nome del file pdf originale).

#!/usr/bin/lua

-- script di estrazione pagina da un file pdf

-- dati
local page = 21
local pdffile = "originale"

-- contenuto del sorgente LaTeX
local extractpage = [[
\documentclass{minimal}
\usepackage{pdfpages}
\begin{document}
\includepdf[pages=%s]{%s}
\end{document}
]]

-- creo il file sorgente tex che estrae
-- la pagina voluta in un secondo file pdf
local pg = tostring(page)
local fn = "tmppage"..pg
local fl = assert(io.open(fn..".tex", "w"))
fl:write(string.format(extractpage, pg, pdffile))
fl:close() 

-- compilazione file tex
os.execute("pdflatex -interaction=nonstopmode "..fn)

-- cancellazione file temporanei
os.remove(fn..".log")
os.remove(fn..".aux")
os.remove(fn..".tex")

Ritagliare una pagina

Ecco il secondo passaggio: ritagliare dalla pagina una finestra pdf. Si può risolvere con un secondo script Lua, simile al precedente, a cui stavolta occorre passare le coordinate x ed y dell’angolo in basso a sinistra e quelle dell’angolo in alto a destra del box (il sistema di riferimento ha l’origine nell’angolo inferiore sinistra del foglio).
Per fare un esempio, immaginiamo di voler estrarre il box della metà superiore di una pagina A4 (210mm x 297mm): le coordinate saranno per il vertice inferiore sinistro ll=(x=0mm, y=148.5mm) e per il vertice in alto a destra ur=(x=210mm, y=297mm).

Questa volta faremo uso della classe standalone, che ha il pregio di produrre una pagina della stessa dimensione del contenuto, e del pacchetto graphicx con cui si inserisce l’immagine che andremo a ritagliare con le coordinate (notate l’opzione viewport):

#!/usr/bin/lua

-- script di ritaglio finestra pdf

-- posizione angoli della finestra
local ll = {x="15mm", y="35mm"} -- ll=lower/left
local ur = {x="195mm", y="72mm"}-- ur=up/right
local page = 21

local ritpage = [[
\documentclass{standalone}
\usepackage{graphicx}
\begin{document}
\includegraphics[viewport=%s %s %s %s]{%s}
\end{document}
]]

-- creo il file sorgente tex di ritaglio
local pg = tostring(page)
local fn = "tmppage"..pg

-- creo il secondo file LaTeX per il ritaglio
local fnrit = "page"..pg..pdffile

local flrit = assert(io.open(fnrit..".tex", "w"))
flrit:write(string.format(ritpage, ll.x, ll.y, ur.x, ur.y, fn))
flrit:close() 

os.execute("pdflatex -interaction=nonstopmode "..fnrit)

-- cancellazione file
os.remove(fn..".pdf")
os.remove(fnrit..".log")
os.remove(fnrit..".aux")
os.remove(fnrit..".tex")

Linguaggio

Fino ad ora la cosa è stata abbastanza rudimentale, almeno come interfaccia utente, infatti i precedenti paragrafi spiegano i concetti ed un implementazione di base, ma siamo in grado di lavorare con un linguaggio più ad alto livello creando una piccola libreria Lua.
Con essa potremo ritagliare oggetti da più file pdf o da più pagine dello stesso documento.
Per il momento tuttavia, lasceremo questa libreria ad un prossimo futuro, cominciando il divertimento dalla costruzione del linguaggio. Non vi rimane che sperimentare questa soluzione e fare le vostre considerazioni.

Note tecniche

Per svolgere le procedure indicate è necessario aver un po’ di dimestichezza con LaTeX (in particolare con i concetti di base dei pacchetti e con il meccanismo di compilazione), ed almeno saper eseguire sul sistema uno script in Lua.
Se siete alle prime armi la prima cosa da fare è installare una distribuzione TeX e leggere una guida di base.
Alla prossima, Ciao.

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