venerdì 22 maggio 2015

Chi ha ucciso il software libero?

Sul banco degli imputati porto, con mia grande soddisfazione, il metodo induttivo.

Il metodo induttivo è quel processo mentale che attraverso la valutazione dei casi particolari noti tenta di stabilire una legge universale per predire eventi ignoti (e futuri).

Non so se avete mai sentito parlare del tacchino induttivista...

Un tacchino, in un allevamento statunitense, decise di formarsi una visione del mondo scientificamente fondata.

Il tacchino induttivista che riceveva il pasto tutte le mattine alle ore 9.00, trasse la conclusione che ogni mattina, alle ore 9.00, avrebbe ricevuto da mangiare, per tutta la vita, conclusione che si dimostrò falsa solo il giorno della vigilia di Natale, quando, invece di venir nutrito, fu sgozzato.

Morale della favola: una asserzione non è provata definitivamente da milioni di conferme ma può essere rigettata definitivamente per una sola confutazione.

Einstein avrebbe detto che nessuna quantità di esperimenti potrà dimostrare che ho ragione; un unico esperimento potrà dimostrare che ho sbagliato.

Quindi la pretesa del metodo induttivo è praticamente insostenibile. Con esso non si creano leggi universali, ma dogmi. E si finisce per cristallizzare ciò che più di buono abbiamo: Le idee.


Chi ha ucciso il software libero? Le procedure lo hanno ucciso. Le procedure che, a torto, riteniamo infallibili.

Il mondo nel quale viviamo è un mondo che non capiamo ma che sosteniamo, con arroganza, di capire. Un mondo in cui la nostra vita è plasmata da quanto è incerto e altamente dirompente piuttosto che da un corso di eventi semplice, ben pianificato e nella media. In verità, cercando di controllare gli eventi, attraverso la pianificazione, la programmazione ed elaborando strategie per conquistare il mondo, stiamo aumentando i fattori di alta instabilità, rendendoci più fragili. Più si perde tempo a discutere, a parlare del niente, a pianificare migrazioni, a sensibilizzare le coscienze, a studiare come rendere appetibile un sistema operativo open source alla gente, più sprofondiamo nel baratro.

Lo ha ucciso chi lo avrebbe dovuto promuovere. Lo ha ucciso chi di software libero si riempie la bocca.

Fare, adattarsi, essere, invece di prevedere, pianificare, teorizzare. La verità è che qui, sotto sotto, nessuno si vuole sporcare le mani, e allora si perde tempo a sprecare fiato, inutilmente.

Nassim Taleb, famoso epistemologo, in un suo libro, Il cigno nero, racconta la storia immaginaria di una scrittrice  e del suo libro pubblicato sul web che viene scoperto da una piccola casa editrice. Questa pubblica il libro, che diventa un bestseller internazionale. La piccola casa editrice si trasforma così in una grande società, e la scrittrice è diventata famosa.

Il mondo è governato dall'improbabile. Noi non siamo nelle condizioni di conoscere il corso degli eventi. È inutile pianificare, aumenteremmo solo i fattori di alta instabilità. Ce lo dimostrano quotidianamente gli economisti, che studiano studiano e studiano, ma la crisi economica si fa sempre più nera, la disoccupazione aumenta e le fabbriche chiudono.

Ma c'è un altro fattore per cui spendere due parole: Gli esperti. La forza conferita a un tecnico o a uno scienziato da un diploma o una laurea ha un effetto dirompente e incontrollabile. Sempre Taleb, in un altro suo libro, L'antifragile, fa presente che  i ponti romani costruiti con il sapere pratico (tipico degli imprenditori), sono ancora lì, a testimoniarci la loro superiorità di approccio rispetto al sapere accademico acquisito sui banchi di scuola.

E poi mi viene da pensare alla Salerno-Reggio Calabria... vabbe'.

Anche la globalizzazione, frutto di procedure sofisticate, pace all'anima sua, è fallita, dobbiamo farcene una ragione. Ciò che resta è il lavoro dell'artigiano. Ciò che resta sono i sogni da realizzare partendo dalla propria realtà territoriale e, soprattutto, sporcandosi le mani.

Il 2016 sarà l'anno di Linux? Da che punto guardi il mondo tutto dipende, diceva una nota canzone.


mercoledì 20 maggio 2015

Software libero, ma di cosa stiamo parlando?

Il solipsismo è la credenza secondo cui tutto quello che l'individuo percepisce viene creato dalla propria coscienza. Di conseguenza, tutte le azioni e tutto quello che fa l'individuo è parte di una morale prestabilita dal proprio io. L'io che crea il mondo. E questo avviene in maniera trasversale, nel senso che si prescinde da classi e ceti sociali.

L'individuo, nell'accezione peggiore del termine, diviene il centro dell'universo. O, forse, più che di individuo dovremmo parlare dell'ego. Di una sorta di patologia grave dell'io. Un egocentrismo malato e subdolo, schizofrenico e dirompente.

Siamo tutti figli di Cartesio. Per Cartesio, nell'atto in cui io penso, non posso dubitare del fatto che "io esisto". Stabilito questo però risultava difficile, se non impossibile, per Cartesio, determinare qualche conoscenza certa che andasse al di là del Cogito, dell'io penso. Quindi l'unica realtà è l'io che pensa. Cogito, dunque sono.

Siamo nell'ambito dell'autoreferenzialità patologica dell'io. Io, io e solo io. Fateci caso quando parlate con qualcuno, provate a contare quante volte pronunciate il pronome personale "io" in dieci minuti di conversazione.

E siccome l'io è l'unica realtà esistente, tutto il resto diviene un prolugamento di me stesso: Figli, amici, coniuge, gli altri.

Questa è la vera malattia di cui è affetto l'occidente. Ogni cosa altro non è che il prolungamento del proprio io malato. Allora posso decidere di privatizzare l'acqua, posso decidere di destabilizzare politicamente interi popoli per poterli poi meglio depauperare, spogliarli delle proprie ricchezze. Alla fine non faccio male a nessuno, se non a me stesso essendo ogni cosa un prolugamento del mio io. Mi posso permettere di considerare finanche i miei figli come parte di me stesso. Figli mai svezzati e cordoni ombelicali ancora da recidere.

Inquinare, rubare, arroganza, tracotanza non vengono sentiti come immorali, come comportamenti inopportuni. Questo perchè ciò che è giusto e ciò che è ingiusto è stabilito dall'io malato. E siccome gli altri non esistono se non li penso, sono miei prodotti, da usare e gettare come e quando voglio.

In questo contesto parlare di software libero è quasi assurdo. Il software libero pressuppone una base comunitaria. E questa idea, anche a livello inconscio, va a cozzare con le categorie mentali impregnate di solipsismo.

Provate ad osservare un fenomeno. Se si cerca di creare una comunità intorno ad un'idea, in realtà non si crea una comunità, ma un'arena dove i vari "io" lottano per la supremazia. E questo accade perchè ognuno vede gli altri come il prolungamento del proprio io. Ed è guerra senza esclusione di colpi.

L'idea comunitaria del software libero è solo apparente. Assistiamo a progetti impermeabili e portati avanti da poche persone. Tutti, o quasi, rilasciano la ricetta, ma nessuno, o quasi, ti dice la procedura per far interagire i vari elementi e arrivare al dolce. Perchè dire agli altri come si fa se gli altri altro non sono che il prolungamento di me stesso?

Ma anche nel momento in cui rendi aperto un progetto, nel senso che permetti ad altri di imbarcarsi, primo o poi ti accorgi che la gente non viene per aiutarti ma per sovvertire il progetto stesso. Non si tratta di mettersi a disposizione per migliorare un qualcosa, ma ancora una volta si innesca la guerra spietata di supremazia. Ancora una volta è una guerra per la supremazia del proprio io.

Stando così le cose, non dobbiamo lamentarci se il software proprietario prenderà sempre più piede. Anche qui sembrerebbe semplice capire il perchè. Perchè nelle società commerciali, nelle multinazionali non vige una base comunitaria, nell'accezione genuina del termine, ma una reductio ad unum. In altri termini, in queste realtà c'è un capo. E tutti gli altri subalterni. Per contratto si accetta di essere il prolungamento del capo.

"La comunità anzitutto non ha una base contrattuale: essa fiorisce «da germi dati, quando le condizioni sono favorevoli; al suo interno si forma una gerarchia naturale basata sulle differenze di età, forza e saggezza, ma domina un atteggiamento di benevolenza e rispetto reciproci." [Tönnies, sociologo.]
 



Software libero, ma di cosa stiamo parlando? Intelligenti pauca, ovverosia alle persone intelligenti poche parole...

sabato 16 maggio 2015

Un semplice esperimento di Intelligenza Artificiale

Salve!

Non so se ricordate i vecchi film di fantascienza, quelli dove il ragazzo smanettone di turno inizia a dialogare attraverso il pc con una presenza sconosciuta.

Ecco, grazie a Python ho cercato di rivivere quelle atmosfere creando un programmino che, come un bambino, è capace di apprendere e ricordare. In pratica, il programma chiede ciò che non sa e non lo dimentica più.

Presto, una volta sistemato meglio, pubblicherò anche il codice. :)


A presto! 

sabato 9 maggio 2015

Restiamo in contatto grazie a python [Brevi esercizi]

Salve a tutti,
in questo articolo riporto un piccolo script in python nato per la necessità che ho avuto di restare "in contatto" con un pc collegato ad internet con un ip pubblico dinamico.


Cosa fà lo script?

Molto semplicemente lanciandolo inizia il suo lavoro che consiste in un loop infinito, lavorerà fin quando non lo fermeremo manualmente, e controllerà l'ip pubblico per rilevare variazioni. Se viene rilevato un cambiamento ci invierà una email comunicandoci il nuovo indirizzo.

Ma andiamo a vedere il codice e poi un breve commento.

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

import urllib,smtplib,time

# Tempo di attesa in secondi tra due controlli
attesa = 600

# Parametri per invio email
destinatario = "email_destinataio@gmail.com"
mittente = 'email_mittente@gmail.com'
username = 'email_mittente@gmail.com'
password = 'password'

# Altre variabili
MemoriaIp=""
conta = 0

while True:
 conta += 1
 print "Controllo n. %i" % conta
 
 # Legge IP pubblico
 sock = urllib.urlopen("http://ipecho.net/plain")
 ReadIP = sock.read()  
 sock.close()   

 if ReadIP != MemoriaIp:
  # Comunica nuovo IP a video
  print "Rilevato nuovo IP %s" % ReadIP
  
  # Ricorda IP per verifica cambiamento
  MemoriaIp = ReadIP
  
  # Prepara variabili per email
  oggetto = 'Notifica IP %s' % ReadIP
  testo = 'Il tuo IP: %s' % ReadIP
  header  = 'From: %s\n' % mittente
  header += 'To: %s\n' % destinatario 
  header += 'Subject: %s\n\n' % oggetto
  msg = header + testo

  #Invia email 
  server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
  server.ehlo()
  server.login(username,password)
  server.sendmail(mittente, destinatario, msg)
  server.quit()
 time.sleep(attesa)

Nella parte iniziale troviamo le variabili generali, in particolare attesa è l'intervallo di tempo che passa tra due controlli, il valore è espresso in secondi quindi il valore 600 equivale a 10 minuti.

A seguire abbiamo le variabili con i dati dell' account del mittente e del destinatario delle email di segnalazione, compresi i parametri per l'autenticazione.

Come avrete notato  in questo esempio  ho usato degli indirizzi gmail, ho fatto questa scelta perchè  credo che siano tra i piu' usati ..... vabe' c'è anche da dire che era quello che serviva a me ;)
Cmq il tutto è , come sempre, facilmente personalizzabile.

Le altre variabili sono solo di servizio e quindi non hanno bisogno di ulteriori spiegazioni.

A questo punto inizia il loop che controlla l'ip leggendo il risultato del sito http://ipecho.net/plain che ci fornisce il nostro ip pubblico, lo memorizza e lo confronta con l'ultimo letto.

A questo punto se i due valori non coincidono vuol dire che il nostro indirizzo ip è cambiato e quindi parte il messaggio email che contiene il nuovo ip.

Questo e' quanto, piccolo script ma spero utile anche a voi.

Saluti!


venerdì 8 maggio 2015

Corso Lua - puntata 10 - Applicazioni


Un po' di ripasso

Negli esempi della puntata ci servirà rinfrescarci la memoria su una particolarità di Lua: si possono omettere le parentesi tonde nella sintassi della chiamata a funzione quando viene passato un unico argomento di tipo stringa o di tipo tabella.

Pertanto un codice come questo viene interpretato come la richiesta di esecuzione della funzione 'book' con argomento la tabella definita esplicitamente con il costruttore:
book{
    author = "Luo Shan Minh",
    title = "Great planet"
    pages = 123.00 * 1.10, -- :-)
    editor = "Father & Sons",
}

Dal punto di vista dell'utente invece, una serie di elementi 'book' di questo tipo salvati in un file di testo rappresentano una sorta di database bibliografico.
Comunque sia i dati così descritti corrispondono a codice Lua perfettamente lecito pronto per essere letto ed eseguito dall'interprete con la funzione predefinita 'dofile()'.

Lua può quindi essere un formato per descrivere i dati ed elaborarli definendo opportunamente funzioni --- nell'esempio la funzione 'book()' --- senza che sia necessario effettuare il parsing che sarebbe invece necessario se i dati fossero per esempio nel formato JSON.

L'inventario

Moltissimi esercizi commerciali alla fine dell'anno redigono l'inventario del magazzino, l'elenco della merce con le quantità rilevate e il costo di acquisto dal fornitore.

Il formato dei dati d'inventario

Prevediamo che il record di un articolo sia il seguente:
  • codice: identificativo univoco articolo;
  • descrizione: breve testo descrittivo;
  • classe: codice del tipo di articolo;
  • fornitore: codice univoco del fornitore;
  • reparto: codice univoco del reparto;
  • costo: costo di acquisto di un pezzo;
  • quantità: pezzi rilevati a magazzino.

Caliamo direttamente questa struttura nel formato auto-descrittivo di Lua:

item{code="C123",
     descr="NOME MODELLO",
     class = "Sciarpa",
     supplier="MYSUP",
     department="MYDEP",
     cost=123.45,
     qty=1,
}

Per eseguire il codice è necessario un file dati di esempio. Scaricatelo da questo link. Nel file già dotato di estensione '.lua' i dati sono casuali rispetto a un insieme limitato di liste. Di seguito vi riporto le prime sei righe nel formato key=value con le parole chiavi che sono sempre le stesse --- spazi, tabulazioni ritorni a capo tra i campi sono liberi:
item{code="546556", descr="546556 - Puzzle",   class="Puzzle",    supplier="Vivo",    department="ARTE",cost=5.00, qty=29,}
item{code="815720", descr="815720 - Forbici",  class="Forbici",   supplier="Multi",   department="SCU", cost=11.00,qty=26,}
item{code="067225", descr="67225 - Diario",    class="Diario",    supplier="Lifestar",department="CAR", cost=12.00,qty=12,}
item{code="034371", descr="34371 - Cucitrice", class="Cucitrice", supplier="Vartech", department="TEC", cost=6.00, qty=70,}
item{code="070835", descr="70835 - Pennarello",class="Pennarello",supplier="Vivo",    department="COL", cost=12.00,qty=69,}
item{code="343307", descr="343307 - Buste",    class="Buste",     supplier="MySheet", department="CAR", cost=7.00, qty=67,}
...

Come è evidente nell'esempio, i valori testuali sono racchiusi tra doppi apici mentre i valori numerici sono rappresentati con il punto decimale (non possiamo usare la virgola, simbolo già previsto dalla sintassi per la separazione dei campi (a proposito l'ultima virgola è opzionale perché in generale è più semplice aggiungere campi alla tabella)).

Cominciamo con lo scrivere il programma che conti gli articoli in inventario e che accerti o meno che il codice degli articoli per ciascuna riga è effettivamente univoco. Ammetteremo che il file 'dati.lua' si trovi nella stessa directory degli script Lua.
Fatelo per esercizio e poi proseguite nella lettura perciò inserisco un segnalibro in modo che possiate riprendere la lettura dopo aver compiuto l'esercizio.

--- Segnalibro esercizio 1 ---

Ecco come contare gli articoli:
-- codice errato
local i = 0
local function item(t) -- funzione locale?
    i = i + 1
end

dofile "dati.lua" -- esecuzione file Lua
print(i)

Per prima cosa osservo che la funzione non può essere locale, e infatti il compilatore Lua ci avverte con "lua: dati.lua:1: attempt to call global 'item' (a nil value)". Quando Lua esegue il file esterno si trova in un secondo chunk in cui non sono visibili le variabili locali al primo pertanto solo la funzione di elaborazione dovrà essere globale.

La seconda osservazione è questa: la funzione 'item()' deve far ricorso a una variabile esterna tramite closure, necessariamente. Infatti può ogni volta avere il solo argomento della tabella del record dell'articolo. Non possiamo poi contare sulla restituzione di un parametro che andrebbe perso quando Lua valuta le espressioni delle chiamate a funzione nel file dati.

Per accertare l'univocità del codice possiamo usare una tabella in cui memorizzare i codici stessi fermandoci se il prossimo codice da includere come chiave è già presente. Il valore può essere un tipo qualsiasi e nell'esempio ho inserito il valore booleano 'true':
local dati = {}
function item(t)
    i = i + 1
    if dati[t.code] then
        error(t.code.." errore riga "..i)
    else
        dati[t.code] = true
    end
end

dofile "dati.lua"
print(i)

Scriviamo una versione della funzione item() perché restituisca il totale di magazzino in termini di costo e quantità. Al solito provate a scrivere il codice e proseguite.

--- Segnalibro esercizio 2 ---

Per ora il codice è immediato:
local qty, tot = 0, 0
function item(t)
    qty = qty + t.qty
    tot = tot + t.qty * t.cost
end

dofile "dati.lua"
print(qty, tot)

Costruire l'albero ovvero mettere tabella dentro tabella

Normalmente il report dell'inventario viene strutturato per reparti o per fornitori, oppure ancora per classi di articolo, riportando i relativi totali. In generale un lavoro piuttosto noioso se non è supportato da software opportuni.

Ammettiamo che il magazzino sia suddiviso in reparti e che a sua volta i reparti siano suddivisi per fornitore, a cui apparterranno i relativi articoli. Questa struttura è un albero.

Bene, scriveremo la nostra funzione item() in modo che costruisca l'albero corrispondente all'inventario di magazzino utilizzando una tabella che conterrà le tabelle dei reparti ciascuno contenente le tabelle dei fornitori, che ancora conterranno le tabelle degli articoli, traducendo esattamente la struttura con cui abbiamo deciso di organizzare il magazzino. Provateci prima di continuare.

--- Segnalibro esercizio 3 ---

Presa confidenza con la struttura a livelli il codice non è ne difficile ne lungo.
Invece di processare i dati record per record, creare l'albero significa caricare in memoria tutti i dati con l'obiettivo di tradurli in un report successivamente:
-- nuovo oggetto tabella d'inventario
local store = {}

-- main data processing function
function item(record)
    -- livello principale: reparti
    -- creo per comodità una variabile stringa per l'ID del reparto
    local dep = record.department
    
    -- creo la tabella relativa al reparto se ancora non esiste
    if not store[dep] then
        store[dep] = {}
    end
    
    -- var di comodo per il riferimento alla tabella di reparto
    local tabdep = store[dep]
    
    -- livello fornitore
    -- variabile di comodo che contiene il codice del fornitore
    local sup = record.supplier
    
    -- se non esiste la tabella fornitore nel reparto la creo
    if not tabdep[sup] then
        tabdep[sup] = {}
    end
    
    -- livello articolo
    -- vars di comodo alla tabella fornitore e al codice articolo
    local tabsup = tabdep[sup]
    local codeart = record.code
    
    -- se il codice esiste aggiorniamo per quantità
    -- altrimenti creiamo nuova tabella articolo per
    -- la descrizione, il costo e la quantità
    if tabsup[codeart] then
        tabsup[codeart].qty = tabsup[codeart].qty + record.qty
    else
        tabsup[codeart] = {}
        local ta = tabsup[codeart]
        ta.descr = record.descr
        ta.cost = record.cost
        ta.qty = record.qty
        ta.class = record.class
    end
end

Raccolta dati

I vantaggi della struttura ad albero creata dalla funzione item() sono due: oltre a classificare per livelli gli articoli è possibile inserire lo stesso articolo con successive chiamate alla funzione. Durante la raccolta dati infatti capita che pezzi diversi dello stesso articolo vengano registrati in tempi diversi.

La raccolta dati può essere effettuata connettendo un lettore di codici a barre a un notebook e gestendo i dati con un foglio elettronico o meglio con un database. In questo modo l'inventario procede molto velocemente e senza fatica, a condizione che sugli articoli sia presente il codice a barre tramite un etichetta per esempio, e che siano già stati inseriti i dati corrispondenti ai codici.

Visitare l'albero

Vorremo conoscere la quantità totale dei pezzi e il costo totale per ogni tipo di articolo (per esempio 'Diario', 'Forbici' o 'Quaderno').
Disegnate la struttura della tabella creata dalla funzione 'item()' e poi scrivete il codice di reporting.

--- Segnalibro esercizio 4 ---

Ecco la mia versione del programma basata su una tabella dove le chiavi sono i nomi delle varie classi articolo e i corrispondenti valori sono tabelle con i campi quantità (qty) e costo (cost).
Il codice non è completo perché non vi ho riportato la funzione 'item()' che è la stessa vista prima, la creazione della variabile 'store' e la chiamata alla funzione 'dofile()'.

local function class_rpt(tree)
    local class_data = {}
    for _, dep in pairs(tree) do
        for _, sup in pairs(dep) do
            for code, item in pairs(sup) do
                if class_data[item.class] then
                    local t = class_data[item.class]
                    t.qty = t.qty + item.qty
                    t.cost = t.cost + item.qty*item.cost
                else
                    class_data[item.class] = {
                        qty  = item.qty,
                        cost = item.qty*item.cost,
                    }
                end
            end
        end
    end
    return class_data
end

-- print report
local class_data = class_rpt(store)

for class, tab in pairs(class_data) do
    local fmt = "Class %20s: qty = %d, cost = %0.2f"
    print(string.format(fmt, class, tab.qty, tab.cost))
end

Memorizzare tutto in un file

Per costruire il file si utilizzano le funzioni della libreria standard di Lua nel modulo io. Le impiegheremo per svolgere il prossimo passo che consiste nel memorizzare i dati ottenuti nell'esercizio precedente nel file "Classi.txt".

La massima efficienza prevede di scrivere il file in un volta sola e di costruirne il contenuto con la funzione 'table.concat()' per evitare di dover concatenare stringhe cosa che produrebbe un enorme trasferimento di memoria come abbiamo già evidenziato in una delle puntate del corso.

Lascio a voi la tastiera e vi aspetto tra qualche riga dopo il segnalibro per presentarvi la mia soluzione del quesito.

--- Segnalibro esercizio 6 ---

Ecco la mia versione che sfrutta la funzione 'class_rpt()' precedentemente definita:
-- create the text file
local function make_class_rpt(fn, data, fmt)
    local fmt = fmt or "Classe %-20s: qty=%6d, cost=%10.2f"
    
    -- data assembly
    local t = {}
    for class, tab in pairs(data) do
        t[#t+1] = string.format(fmt, class, tab.qty, tab.cost)
    end
    local s = table.concat(t, "\n")
    
    -- apertura file
    local f = assert(io.open(fn  .. ".txt", "w"))    
    -- scrittura dati
    f:write(s)
    
    -- chiusura del file
    f:close()
end

local filename = "Classi"
make_class_rpt(filename, class_rpt(store))


L'inventario

L'ultima parte del problema è generare il report principale. Occorre infatti visitare l'albero per trarne i totali dei vari livelli e produrre con essi la stampa. Per ragioni di tempo terremo semplice il codice lasciando irrisolti alcuni problemi come l'ordinamento in ordine alfabetico delle classi del nostro magazzino.

Chiamiamo report() la funzione Lua che visita l'albero iterativamente. Come abbiamo già fatto prima useremo l'iteratore pairs per leggere i valori nelle tabelle dei vari livelli, iteratore che restituisce due argomenti la chiave e il suo valore senza un ordine predefinito.

Ecco le specifiche: l'inventario deve essere suddiviso per reparto con i totali di quantità e costo. Ciascun reparto deve essere suddiviso per fornitore con i relativi totali di quantità e costo. Per ogni fornitore è necessario creare la lista degli articoli con i quattro campi "Descrizione", "Quantità", "Costo". Infine il report dovrà essere memorizzato in formato testo in un file chiamato "Inventario.txt".

Disegnate la struttura della tabella creata dalla funzione 'item()' e poi scrivete il codice di reporting.

--- Segnalibro esercizio 7 ---

Bentornati!
La soluzione da conforntare con la vostra che vi riporto di seguito ha la possibilità di regolare la larghezza delle tre colonne dei dati impostando le tre variabili interne alla funzione 'report()'.
I campi non sono separati da un carattere di tabulazione perché il report cambierebbe allineamento in funzione del numero di caratteri spazio che l'editor in uso assegna alla tabulazione.

Inoltre il calcolo dei subtotali risulta essere il più semplice ed efficiente possibile perché il codice esegue somme di somme.
Ecco il programma Lua completo:
-- nuovo oggetto tabella d'inventario
local store = {}

-- main data processing function
function item(record)
    -- livello principale: reparti
    -- creo per comodità una variabile stringa per l'ID del reparto
    local dep = record.department
    
    -- creo la tabella relativa al reparto se ancora non esiste
    if not store[dep] then
        store[dep] = {}
    end
    
    -- var di comodo per il riferimento alla tabella di reparto
    local tabdep = store[dep]
    
    -- livello fornitore
    -- variabile di comodo che contiene il codice del fornitore
    local sup = record.supplier
    
    -- se non esiste la tabella fornitore nel reparto la creo
    if not tabdep[sup] then
        tabdep[sup] = {}
    end
    
    -- livello articolo
    -- vars di comodo alla tabella fornitore e al codice articolo
    local tabsup = tabdep[sup]
    local codeart = record.code
    
    -- se il codice esiste aggiorniamo per quantità
    -- altrimenti creiamo nuova tabella articolo per
    -- la descrizione, il costo e la quantità
    if tabsup[codeart] then
        tabsup[codeart].qty = tabsup[codeart].qty + record.qty
    else
        tabsup[codeart] = {}
        local ta = tabsup[codeart]
        ta.descr = record.descr
        ta.cost = record.cost
        ta.qty = record.qty
        ta.class = record.class
    end
end

-- main reporting function
local function report(store)
    local rpt = {}
    local inv_qty, inv_cost = 0, 0
    local inv_title = "Inventory report"
    
    rpt[#rpt+1] = inv_title
    rpt[#rpt+1] = string.rep("=", #inv_title).."\n\n"
    
    -- column spacing
    local descr_space, qty_space, cost_space = 28, 10, 12
    local line_spaces = 2 + descr_space + qty_space + cost_space
    -- in format string %% means '%'
    local fmt_line = string.format("%%-%ds %%%dd %%%d.2f", descr_space, qty_space, cost_space)
    
    for dep_name, tab_dep in pairs(store) do
        local dep_qty, dep_cost = 0, 0
        local dep_title = string.format("Department %s:", dep_name)
        
        rpt[#rpt+1] = dep_title
        rpt[#rpt+1] = string.rep("=", #dep_title).."\n"
        
        for sup_name, sup_tab in pairs(tab_dep) do
            local sup_qty, sup_cost = 0, 0
            local sup_title = string.format("Supplier %s:", sup_name)
            
            rpt[#rpt+1] = sup_title
            rpt[#rpt+1] = string.rep("-", #sup_title)
            
            for code, item in pairs(sup_tab) do
                sup_qty = sup_qty + item.qty
                sup_cost = sup_cost + item.qty*item.cost
                rpt[#rpt+1] = string.format(fmt_line, item.descr, item.qty, item.cost)
            end
            
            rpt[#rpt+1] = string.rep("-", line_spaces)
            rpt[#rpt+1] = string.format(fmt_line, "Total supplier", sup_qty, sup_cost)
            rpt[#rpt+1] = ""
            
            dep_qty = dep_qty + sup_qty
            dep_cost = dep_cost + sup_cost
        end
        rpt[#rpt+1] = string.rep("-", line_spaces)
        rpt[#rpt+1] = string.format(fmt_line, "Total department", dep_qty, dep_cost)
        rpt[#rpt+1] = string.rep("-", line_spaces).."\n"
        
        inv_qty = inv_qty + dep_qty
        inv_cost = inv_cost + dep_cost
    end
    
    rpt[#rpt+1] = string.rep("=", line_spaces)
    rpt[#rpt+1] = string.format(fmt_line, "Total inventory", inv_qty, inv_cost)
    rpt[#rpt+1] = string.rep("=", line_spaces).."\n"
    
    return rpt
end

-- create the text file
local function make_txt_file(fn, data)
    -- apertura file
    local f = assert(io.open( fn  .. ".txt", "w"))
    
    -- scrittura dati
    f:write(table.concat(data, "\n"))
    
    -- chiusura del file
    f:close()
end

-- loading data
dofile "dati.lua"

-- report building
local rpt = report(store)
local filename = "Inventario"
make_txt_file(filename, rpt)

-- end


E questo è un frammento del file risultato che si ottiene con il codice precedente e i dati contenuti nel file:
Inventory report
================


Department SCRI:
================

Supplier BestWrite:
-------------------
971495 - Astuccio                    80        16.00
678574 - Astuccio                    81         5.00
643586 - Stilografica                11         1.00
934211 - Stilografica                61        24.00
596141 - Penna                       41        13.00
...
... a lot of lines
...
290676 - Bianchetto                 118        14.00
498096 - Cucitrice                  108         8.00
518327 - Portamine                   30         4.00
639929 - PuntiCucitrice              28        14.00
98644 - Gomma                         4         1.00
113027 - PuntiCucitrice              90        12.00
----------------------------------------------------
Total supplier                     5244     55662.00

----------------------------------------------------
Total department                  44600    538322.00
----------------------------------------------------

====================================================
Total inventory                  308783   3858599.00
====================================================


Conclusioni

Lo script è veloce e indipendente dal sistema operativo: legge un file di testo puro contenente i dati, li assembla nella struttura che corrisponde all'organizzazione reale del nostro magazzino e restituisce un file pronto per essere stampato nella forma voluta e nulla vieta con un ulteriore passaggio di compilazione, che l'inventario sia un report in formato PDF composto con LaTeX.

L'idea si applica alla costruzione di report di dati complessi come un database cinematografico o un bilancio aziendale, oppure a inventari ancora più complessi per esempio per una catena di negozi.

Riassunto della puntata

Nella puntata abbiamo risolto alcuni problemi concreti con Lua. In particolare gli esempi di codice per l'elaborazione di dati mostrano interessanti applicazioni basate su file testuali in formato data description.

Epilogo

Oggi finisce il corso che spero sia stato o vi sarà utile.
Sono soddisfatto del lavoro compiuto e credo di avervi mostrato l'utilità pratica di Lua, questo fantastico piccolo grande linguaggio.
Rimangono inesplorati alcuni temi base di Lua come le coroutine, chissà se ci sarà una puntata extra in merito... magari con l'aiuto di qualcuno di voi.

Ringrazio Luigi che mi chiese di scrivere queste chiaccherate informatiche da pubblicare sul Blog "Lubit Linux" e ovviamente tutti i lettori.

-- exec with Lua
print("Alla prossima.\nR.")

Ciao