lunedì 14 dicembre 2015

Salvataggio automatico file corrente - Gedit. [Se lo avessi saputo prima]

Talvolta le cose sono così semplici che sfuggono. È quello che è successo a me, proprio oggi, dopo aver scritto uno script in bash; pochi secondi prima di salvarlo, è andata via la corrente elettrica e ho perso tutto.

Mi sarebbe bastato andare su Modifica -> Preferenze -> Editor 

E poi aggiungere il segno di spunta su salvare automaticamente il file corrente ogni tot minuti. Mah! Vabbe' ;)

Alla Prossima.

giovedì 19 novembre 2015

Sox e la funzione repeat

Salve!

Premessa.

SoX è un'utilità a riga di comando che può convertire vari formati di file audio. Può anche applicare svariati effetti a tali file al momento della conversione. Inoltre SoX può riprodurre e registrare file audio.

Per riprodurre un file audio, anzitutto installiamo tutte le librerie Sox.

$ sudo apt-get install libsox-fmt-all

Ora per riprodurre un brano con la funzionalità repeat si fa così:

$ cd Musica

$ play Cammino_nella_notte.mp3 repeat 2

In tal modo lo stesso brano sarà eseguito per due volte.




Alla prossima.

sabato 14 novembre 2015

Solidarietà al popolo francese

Solidarietà al popolo francese da parte del Lubit Project e dell'Associazione Gli Amici di Lubit.


sabato 31 ottobre 2015

Eliminare i file di una directory contenenti una parola determinata.

Salve!

Supponiamo che in una directory, ad esempio in Documenti, abbia tantissimi file di testo e che voglia eliminare solo quelli che contengono, nel testo e non nel titolo, una determinata parola, come fare? Se si vuole evitare di cercare prima la parola in ciascuno di esso e poi eliminarli, si può usare uno script facile facile. ;)

#!/bin/bash

read -p "Quale parola vuoi cercare? " stringa
for file in $(grep -l $stringa *); do
    rm -i $file;
done
exit 0

Ovviamente lo script, prima di eliminare un qualsiasi file, chiede il consenso. Nel mio caso ho eliminato tutti i file nel cui testo compariva la parola Fedora. (non ho niente contro Fedora, eh, è solo un esempio) ;)


Alla prossima! 

giovedì 29 ottobre 2015

Creare una copia di backup di un file

 Salve!

Prima di modificare un qualsiasi file di configurazione dovrebbe essere necessario farne una copia di backup.

In questo post vi dirò un modo estremamente semplice, ma anche elegante, di creare una copia di backup di un file.

Poniamo il caso che il file da modificare si trovi nella directory Documenti

$ cd Documenti

e che si chiami file.conf

Uno dei modi per creare una copia di backup è il seguente

$ cp file.conf{,.bak}


La semplicità è sempre un dopo, mai un prima.

Alla prossima! 

mercoledì 28 ottobre 2015

Come tee nessuno!

Salve!

Oggi parliamo, ma solo per un esempio, del comando tee. Per approfondire vi invito ad andare sulla pagina di Wikipedia.

$ risultato=$(echo "Come tee nessuno"| tee  >(sed 's/ /_/g') >(sed  's/\(.*\)/\U\1/') >(rev))

$ echo "$risultato"

In questo caso l'output del comando "echo" viene passato contemporeaneamente, grazie a tee, a tre comandi diversi, che lo elaborano e ne restituiscono i rispettivi risultati:
1. Il primo sed aggiunge l'underscore.
2. Il secondo sed converte la frase da minuscolo in maiuscolo.
3. Il comando rev inverte i caratteri della frase.


Alla prossima.

Sequenza di numeri [bash]

Salve!

Espansione di parentesi graffe VS il comando Seq.

$ echo {1..5} | tr ' ' '\n'
$ seq 1 5

$ echo {1..5}
$ seq -s ' ' 1 5
dove -s sta per output separator.

$ echo {001..5}
$ seq -s " " 001 5

$ echo -e {0..5}"\xc2\xb0"
$ seq -w  0 5 | sed  's/$/\xc2\xb0/' | tr "\n" " "; echo
"\xc2\xb0" = simbolo del grado

Incremento positivo
$ echo {1..10..2}
$ seq -s " " 1 2 10

Incremento negativo
$ echo {10..1..2}
$ seq -s " " 10 -2 1


E poi...
$ echo {a..z}
$ echo {a..z..3}




E ancora
$ echo -e {Lubit,Linux}"\xc2\xae"
\xc2\xae = simbolo del marchio registrato.

Alla prossima!

venerdì 23 ottobre 2015

Lubit e internet per tutti!

Dopo un lungo periodo di "riposo" rieccomi qui con un breve, davvero breve, articolo che ritengo possa essere utile.

Probabilmente molti di voi avranno già sperimentato la soluzione che vado a mostrarvi, ma comunque, siccome è tornata utile a me in un periodo di difficoltà, preferisco scrivere due righe per condividerla.

Problema: 
mi trovo fuori casa , non ho collegamento internet wifi e dispongo solo di una chiavetta usb per collegare il pc.... ok e se volessi collegare anche il mio smartphone , tablet o un secondo pc utilizzando la stessa connessione??

Lubit ha già tutto quello che ci serve per risolvere il problema!

Parto dal presupposto che sia già stata configurata la connessione tramite chiavetta , fatto questo basterà creare una rete wifi in modo da far diventare il pc un access point.

In pratica si fa così, clicchiamo con tasto sinistro del mouse sul network manager ( l'iconcina in alto a destra che rappresenta il collegamento), selezioniamo "crea nuova rete Wi-Fi",  compiliamo il form col nome che vogliamo dare alla nuova rete, il protocollo di sicurezza e la chiave di protezione della rete (naturalmente possiamo anche scegliere nessuna sicurezza per avere una rete aperta) e confermiamo tutto.
A questo punto la rete è stata creata, in alto si nota l'attività di configurazione e avvio della rete, pochi secondi e abbiamo pronta la nuova rete wi-fi quindi possiamo selezionarla sui nostri dispositivi che cosi' avranno accesso alla rete, la stessa che usa il pc collegato direttamente con la chiavetta.

La rete non si avvia automaticamente quindi dopo aver avviato il pc dobbiamo selezionare la rete creata dalla lista delle reti disponibili.

L'ho detto, semplice ma utile! :)

A presto.
Saluti!

Ricerca case-insensitive e sostituzione con sed.

Salve!

Supponiamo di avere i seguenti dati contenuti nel file prova

luigi Marco luCa
Nino lucA MassImo
Vincenzo Andrea LuCA
Nino LUca Ivo Neo
Luigi LUCA Ivo Rob


Come potete notare il nome Luca è stato scritto in modo non corretto.

Il nostro intento è quello di fare una ricerca case-insensitive e correggere il nome in questione.

Perchè si possa avere la ricerca case-insensitive e la sostituzione in modo globale, si scrive così: 

$ sed 's/luca/Luca/Ig' prova

In altri termini, diciamo a sed di sostituire le stringhe luCA, LuCA, LUCa e LUCA con la stringa Luca.


Alla prossima!


mercoledì 21 ottobre 2015

Archivio compresso criptato [Brevi Esercizi]

Salve!

Oggi creamo un archivio compresso criptato. In passato abbiamo già parlato di archiviazione e compressione, perciò non mi dilungo sull'argomento.

Per fini pratici e a mo' di esempio, anzitutto mi creo la directory "prova"

$ mkdir prova

ora mi sposto al suo interno

$ cd prova

E all'interno di essa creo nove file.

$ touch file{01..09}.txt

$ ls

Ora creo l'archivio compresso criptato

tar czpf - file* | gpg --symmetric --cipher-algo aes256 -o archivio.tar.gz.gpg

Cancello i file.txt (operazione non necessaria e, talvolta, anche pericolosa)

$ rm file*

$ ls



Per decriptare e decomprimere l'archivio:

gpg -d archivio.tar.gz.gpg | tar xzf - 


Ovviamente per decriptare servirà la passphrase che abbiamo creato nella fase di criptazione.

Penso meritino un approfondimento sia la GPG che l'algoritmo AES.
Alla prossima. 

venerdì 16 ottobre 2015

Myman, il clone di Pac-Man, su Lubit

Myman è un videogioco in modalità testuale ispirato a Pac-Man, il famoso gioco arcade degli anni '80.

Per averlo su lubit:

$ sudo apt-get install build-essential

$ sudo apt-get install libncurses5-dev groff

$ wget http://downloads.sourceforge.net/project/myman/myman-cvs/myman-cvs-2009-10-30/myman-wip-2009-10-30.tar.gz

$ tar xvfvz myman-wip-2009-10-30.tar.gz

$ cd myman-wip-2009-10-30/

$ ./configure

$ make

$ sudo make install

Per giocare:

$ myman



Pagina del progetto: http://myman.sourceforge.net/

Etichetta 3D [Convert]

Salve!

Anzitutto scarichiamo il font Candice. Una breve digressione, si dice il font o la font? Ho trovato la soluzione definitiva, almeno spero, qui

Ovviamente possiamo usare i font già presenti su lubit. Per avere la lista completa,

$ convert -list font

Posto che abbiamo scaricato il font Candice nella cartella Scaricati,

$ cd Scaricati

Dopo aver scompattato l'archivio zip (tasto dx del mouse e estrai qui), spostiamo il file nella directory /usr/share/fonts/truetype/

$ sudo su

$ mv CANDY.TTF /usr/share/fonts/truetype/

$ exit

Ora bisogna aggiornare la cache dei font

$ fc-cache -f -v

Infine lo script, il cui nome è 3d.sh

#!/bin/bash

convert -size 480x80 xc:black -font Candice-Regular -pointsize 78 \
              -fill white   -annotate +25+65 'Lubit Linux' \
              -shade 140x45  lubit_linux.jpg

exit 0

Inutile dire che possiamo cambiare qualsiasi parametro come meglio ci aggrada e che tutto è estremamente intuitivo.


Risultato.



Alla prossima!

P.S.1: Chi volesse sbizzarrirsi con imagemagick, vada a questa pagina. Qui  
P.S.2: Avete installato Lubit 6?

domenica 4 ottobre 2015

Leggi fin dove ti dico io [sed, awk]

Immaginiamo di avere un testo e, individuata una parola, vogliamo che sed o awk legga (e ci restituisca in output) fino a quella parola, compresa o esclusa. O meglio, fino a quella riga in cui è compresa quella parola (sarebbe meglio dire stringa, eh).

Testo di esempio. [Poesia.txt]

La donzelletta vien dalla campagna,
In sul calar del sole,
Col suo fascio dell’erba; e reca in mano
Un mazzolin di rose e di viole,
Onde, siccome suole,
Ornare ella si appresta
Dimani, al dì di festa, il petto e il crine.
Siede con le vicine
Su la scala a filar la vecchierella
Incontro là dove si perde il giorno;
E novellando vien del suo buon tempo,
Quando ai dì della festa ella si ornava,
Ed ancor sana e snella
Solea danzar la sera intra di quei
Ch’ebbe compagni dell’età più bella.


Primo esempio.

Voglio che awk legga fino alla stringa "crine"

$ awk '{print} /crine/ {exit}' Poesia.txt


 Per ottenere lo stesso risultato con sed, avremmo dovuto scrivere:

$ sed '/crine/q' Poesia.txt


Secondo esempio.

Voglio che awk legga fino alla stringa "crine", escludendo, però, la riga che la contiene.

$ awk '/crine/ {exit} {print}' Poesia.txt


 Per ottenere lo stesso risultato con sed, avremmo dovuto scrivere:

 $ sed '/crine/Q' Poesia.txt

Alla prossima! 


mercoledì 30 settembre 2015

Manipolazione del testo all'interno delle parentesi graffe #Bash

Salve!

Ora che Lubit è stata rilasciata, di sicuro, salvo imprevisti, sarò più presente sul blog.

Assegno alla variabile "stringa" il titolo di uno dei più famosi film di Lina Wertmüller

stringa="Travolti da un insolito destino nell'azzurro mare d'agosto"

Ora voglio che in output le singole parole siano legate tra loro da underscore

echo ${stringa// /_}


Nei prossimi post continueremo a fare esempi sulle parentesi graffe. :)

Ciao!

P.S. Avete scaricato Lubit 6? Qui

lunedì 28 settembre 2015

Lubit 6, nome in codice AKI, è online

Salve!

Finalmente ci siamo riusciti a rispettare la tabella di marcia. Lubit 6 presenta tante novità, hot corners, effetto exposè, lubit plasma panel e tante altre cose, tutte da scoprire. ;)

Per tutte le informazioni e per scaricarla, vi rimando al sito ufficiale del LubitProject: Qui.



Alla prossima!

sabato 5 settembre 2015

Verso Lubit 6

Nonostante il caldo assurdo di quest'estate, lo sviluppo di Lubit è andato avanti. Si spera tanto che Lubit 6 sia pronta per la fine di settembre.

Oggi vi vogliamo mostrare, molto semplicemente, come anteprima, alcune immagini salienti.

Insieme a Lubit, si sta portando avanti anche lo sviluppo di un browser, il lubit browser minimal, interamente scritto in Python. I betatester confermano un calo di consumo di almeno del 60% rispetto agli altri browser. Sinceramente non so se riusciremo ad includerlo nel parco software di lubit 6.

Nuovo Conky

Lubit Plasma Panel

Nuove icone Wbar

Il nuovo menu di spegnimento
Ovviamente si stanno aggiornando tutti i pacchetti deb creati per lubit 5 e in più sono state create ex novo altre applicazioni di cui vi diremo prossimamente.

Lubit Browser
Questi sono solo piccoli accenni, prossimamente scriveremo un post dove faremo luce su tutte le novità di Lubit 6.

A presto!

venerdì 14 agosto 2015

Analisi di un testo [AWK]

Salve!

È da un po' che non scrivo sul blog. Purtroppo d'estate è sempre così. E poi considerando l'eccezionalità del caldo di quest'anno non avrei dovuto scrivere almeno fino al quindici settembre. Dai, speriamo che finisca presto e che ritorni prima di subito il freddo.
Intanto vi dico che lo sviluppo di Lubit 6 prosegue. Si spera che esca per la fine di settembre. Prossimamente vi anticiperemo qualcosa.

Vabbe', oggi voglio semplicemente pubblicare un piccolo script in awk che, dandogli in pasto un testo, vi ritorna il numero totale di righe, di parole, di caratteri, di consonanti e il numero totale di ciascuna vocale.

 
Il testo
Output
Lo script:

#!/usr/bin/awk -f
#Bit3Lux
#count.awk
#Numero di ciascuna vocale, numero di righe, numero di parole,
#Numero di caratteri, numero di consonanti.

BEGIN {
  print "\n\tREPORT\n"

}

{
 Numero_parole += NF
 Numero_caratteri += length
 A += gsub (/[Aaà]/,"*", $0)
 E += gsub (/[EÈeèé]/,"*", $0)
 I += gsub (/[Iiì]/,"*", $0)
 O += gsub (/[Ooò]/,"*", $0)
 U += gsub (/[Uuù]/,"*", $0)
 vocali = A+E+I+O+U

}

END {
  print "\n\tNumero di righe: " NR
  print "\tNumero di parole: " Numero_parole
  print "\tNumero di caratteri: " Numero_caratteri
  print "\tNumero di consonanti: " Numero_caratteri - vocali
  print "\tNumero vocali: " vocali
  print "\tVocale a: " A
  print "\tVocale e: " E
  print "\tVocale i: " I
  print "\tVocale o : " O
  print "\tVocale u: " U"\n"
   
}

Insomma è tutto estremamente semplice, è stato un modo per rompere il ghiaccio (si fa per dire). E poi, ora che guardo meglio, c'è anche un piccolo errore, scopritelo voi... ;)

Per quanto riguarda il blog, bollono in pentola tante idee e tante novità. Si spera che al più presto si possa iniziare con un corso di Sistema linux, un corso su Sed e tanti esercizi su bash, awk, python, lua e... insomma si prospetta un autunno caldissimo (no aspetta, meglio freddissimo :D)

Alla prossima!

lunedì 29 giugno 2015

Estrarre indirizzi e-mail da un file di testo [bash]

Salve!

Non so voi, ma se devo scrivere qualsiasi cosa uso sempre l'editor di testo.

Stamattina ho scritto una lettera lunghissima contenente molti indirizzi e-mail.

Allora mi sono chiesto, c'è un modo per estrarre dalla lettera solo gli indirizzi e-mail? E così ho iniziato a cimentarmi con perl, awk, grep, espressioni regolari e altre diavolerie affini. Tutte cose interessanti, intendiamoci, ma sinceramente impresentabili, e poi diventa difficile spiegare quelle cose in un post, considerando anche che in serata è venuto a farmi visita l'anticiclone africano.

Alla fine mi sono riversato su bash, e  devo dire che c'è un sistema veramente semplice per compiere l'operazione di cui sopra.

Immaginiamo di voler estrapolare gli indirizzi email dal seguente testo: (scusate per l'esempio, ma la mia fantasia è inversamente proporzionale al caldo)


ciao Luigi, mi scrivi il tuo indirizzo email?

Il mio indirizzo è il seguente bit3lux@gmail.com

Altri indirizzi messi alla ca**o di cane ciao.birra@gmail.com, ciao.ciao@yahoo.it

come.stai_bene@gmail.com ######## lubit_linux-for_ever@forzalinux.com ########

etc. 


Lo script, che può essere lanciato semplicemente da riga di comando, è il seguente:

for i in $(cat dati.txt); do echo $i | grep "@"; done


Ecco il risultato:



Alla prossima!

martedì 16 giugno 2015

Costruiti per rompersi

Poniamo che esista una multinazionale, dal nome Mistoft, che produce software proprietario...

La Mistoft vende pere? No, vende software proprietario. E il software si consuma? Niente affatto, il software può essere definito "immortale". E i clienti della Mistoft sono infiniti? Sono tanti, ma non infiniti. E allora, se il software non si consuma, come accade per le pere, e i clienti non sono infiniti, come fa la Mistoft, dopo aver venduto il suo sistema operativo a tutti i suoi clienti, a fare nuovi profitti? E qui entra in gioco la obsolescenza programmata. 

Cos'è la obsolescenza programmata?


È una pratica attraverso cui ti convinco che ciò che hai comprato è già vecchio e ti costringo a comprarne uno nuovo instillandoti falsi bisogni. E quello che compri è quasi sempre identico a ciò che già hai. Quindi butti una cosa nuova per comprarne una identica, ma con il vestitino diverso.

E allora mi viene da pensare: "È difficile spiegare come l'assurdo possa lentamente infiltrarsi nella realtà, fino a sembrare del tutto normale." ¹


¹ Dylan Dog, Albo 7, La zona del crepuscolo, 1987



domenica 7 giugno 2015

Lubit, un sistema per tutti !


Che Lubit sia un sistema operativo semplice, stabile e veloce non è un novità per chi segue questo blog, quindi il titolo di questo post deve avere anche un altro significato , anzi diciamo che ha doppio significato.
L'immagine in alto rappresenta chiaramente l'argomento che andrò a trattare in questo articolo, si tratta di una installazione centralizzata del sistema che "serve" più client.
Andiamo quindi subito a vedere come Lubit può essere un sistema per tutti.

Cos'è un Terminal Server
Siamo abituati a pensare i servers come web server, file server, print server e così via, adesso invece andremo a realizzare un OS Server o per meglio dire un Terminal Server.

Lo scopo di questa soluzione è utilizzare meglio le risorse e la capacità di calcolo di un PC mettendoli a disposizione di PC datati, anche senza memoria di massa , che faranno da semplici Thin-Client connesi in rete locale.
Per intenderci andremo a realizzare quello che accade con i terminali stupidi collegati a server unix, chi ha qualche annetto se li ricorderà ;)
Il sistema operativo sarà installato solo sul server e ogni client, collegato alla rete LAN, farà il boot da rete e  il login come se si operasse direttamente sul Pc server.

Come funziona.
Si parte da una installazione di Lubit su di una macchina che abbia la capacità e le risorse per gestire con tranquillità la multiutenza visto che permetterà di far lavorare più utenti contemporaneamente, tutto il tutorial si basa sulla versione a 32 bit per assicurare il funzionamento anche con i client un pò datati.
Il secondo requisito per realizzare quanto detto è un sistema che permetta ai client di avviarsi prelevando il sistema operativo da remoto, questa funzione si chiama PXE.
Avrete notato che la maggior parte di pc ha la possibilità in fase di boot di scegliere se avviarsi da una memoria locale o da rete, beh questa è proprio la funzione che ci serve.
Il PXE dev'essere installato e configurato sul server, questo si compone di un DHCP server e di un server TFTP (Trivial File Transfer Protocol).
Ricordo velocemente che il DHCP server (Dynamic Host Configuration Protocol), come dice il nome, è un protocollo di rete che permette di gestire le connessioni degli host assegnando ai client che ne fanno richiesta tutti i parametri di rete, per es. indirizzo ip, subnetmask, DNS e altri parametri specifici per il PXE.
In questo caso la prima cosa che fà il DHCP è fornire ai client, oltre ai parametri di rete, l'indirizzo TFTP dove risiedono i files di sistema da caricare.
Il TFTP server è una versione semplificata di FTP che permette di rendere disponibili dei files in rete, non ha necessità di autenticazione.
Quindi avendo installato e configurato questi due componenti abbiamo la possibilità di accettare delle richieste DHCP, riconoscere e fornire tutti i dati ai client per il boot, e inoltre rendiamo disponibile agli stessi client i files di sistema da leggere tramite TFTP.

Oltre a questi due componenti andremo anche a generare i files di sistema per i client.

Dopo aver spiegato, anche se brevemente, come funziona il tutto andiamo alla parte pratica.


Realizziamo il nostro Terminal Server!
Potremmo installare tutte le funzioni che ho descritto prima a mano singolarmente ma  esiste una soluzione rapida e semplice per fare tutto in modo quasi automatico, e siccome quel che conta è il risultato andremo spediti per la via più breve.


Per installare tutto quanto in un colpo solo basta digitare da terminale:


sudo apt-get install ltsp-server-standalone openssh-server



fatto questo abbiamo pronti tutti i servizi che ho elencato in precedenza, naturalmente ora non resta che fare un pò di configurazione.

Innanzitutto partiamo dalla configurazione dei parametri di rete.
Abbiamo appena installato un server dhcp nel nostro PC e quindi, cosa molto importante, se il router ha il servizio dhcp attivo è assolutamente necessario disattivarlo!!!
Configuriamo adesso l'interfaccia di rete del server che garantirà il servizio, dobbiamo configurarla con un ip statico a nosto piacimento, l'unica raccomandazine è di utilizzare ip privati.

Per esempio l'indirizzo ip privato per il server che utilizzerò in questo articolo è 192.168.1.3 netmask 255.255.255.0
Editiamo il file che imposta i parametri per le interfacce di rete:


sudo leafpad /etc/network/interface


inserendo le seguenti righe


auto eth0

iface eth0 inet static

        address 192.168.1.3

        netmask 255.255.255.0

        gateway 192.168.1.1



Il router invece ha indirizzo 192.168.1.1 netmask 255.255.255.0 e garantirà l'accesso ad internet per tutti i PC.


A questo punto cominciamo con il creare l'ambiente di sistema che verrà letto dai client, procediamo da terminale digitando:


sudo ltsp-build-client


ora potete prendere un caffè nel frattempo che viene completata l'operazione (ci vorrà un pò, circa 20 minuti), il caffè non è indispensabile per la riuscita quindi potete optare anche per altro ;)

Finito questo passo, è il momento di configurare i servizi.
Cominciamo con il DHCP, il file da modificare si trova in /etc/ltsp ed ha nome dhcpd.conf , quindi da terminale:



sudo leafpad /etc/ltsp/dhcpd.conf


(chi preferisce un editor old style testuale può usare nano al posto di leafpad)

ci troviamo adesso nei parametri di configurazione che dobbiamo personalizzare in base alle nostre necessità, in particolar modo dobbiamo modificare gli indirizzi di rete del server e il range degli ip a disposizione.

Riporto qui il file già pronto per configurae il server così da rendere piu' facile la modifica, basta copiare ;)



# Default LTSP dhcpd.conf config file.

#



authoritative;



subnet 192.168.1.0 netmask 255.255.255.0 {

    range 192.168.1.20 192.168.1.250;

    option domain-name "lubit.local";

    option domain-name-servers 192.168.1.3;

    option broadcast-address 192.168.1.255;

    option routers 192.168.1.1;

#    next-server 192.168.1.1;

#    get-lease-hostnames true;

    option subnet-mask 255.255.255.0;

    option root-path "/opt/ltsp/i386";

    if substring( option vendor-class-identifier, 0, 9 ) = "PXEClient" {

        filename "/ltsp/i386/pxelinux.0";

    } else {

        filename "/ltsp/i386/nbi.img";

    }
}


Come si può vedere ho impostato la lan 192.168.1.0 e subnet 255.255.255.0 dell' ip 192.168.1.3 che è l'indirizzo dell'interfaccia del server, impostato il range di ip disponibili dal 20 al 250, ho dato un nome al dominio e specificato il broadcast, il resto potete lasciarlo così com'è.

Specifichiamo che il server dhcp deve stare in ascolto sull'interfaccio eth0 editando il file


sudo leafpad /etc/init.d/isc-dhcp-server


modifichiamo le variabili


DHCPD_CONF=/etc/ltsp/dhcpd.conf 

INTERFACES="eth0"


A questo punto aggiorniamo le chiavi ssh:


sudo ltsp­-update­-sshkeys


generiamo l'immagine del file di sistema che verrà letto dai client


sudo lstp-update-image


e riavviamo il server DHCP:


sudo /etc/init.d/isc-dhcp-server restart


Il gioco è fatto!

Ora da Lubit Control Panel creiamo un utente per ogni client, questo passo non è indispensabile ma è lo scopo di tutto il lavoro che abbiamo fatto fino ad ora :)

Colleghiamo i client alla rete e avviamo il boot da network.

Se il vosto client è davvero vechiotto o molto particolare e non supporta il boot da rete a, link http://rom-o-matic.net/gpxe/gpxe-git/gpxe.git/contrib/rom-o-matic/ potete creare un floppy disk, chiavetta usb o cd per risolvere .


Ok, sicuramente a questo punto vi trovate davanti al login di Ubuntu, tranquilli è solo l'avvio ... il resto è solo Lubit!

E' tutto, buon divertimento, alla prossima.

Saluti!

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

giovedì 30 aprile 2015

Corso Lua - puntata 9 - Oop


Introduzione alla oop

Dal punto di vista dell'utente, il paradigma della Object Oriented Programming si basa sulla creazione di entità indipendenti chiamate oggetti e l'utilizzo di servizi attraverso i metodi.

Ciascun oggetto incorpora dati e metodi che ne definiscono il comportamento che è identico per tutti quelli di una stessa famiglia chiamata classe, una sorta di prototipo che rappresenta un ``tipo di dati''.

Nel gergo della programmazione oop la creazione di un oggetto avviene con un metodo particolare chiamato costruttore. Nei linguaggi con tipi statici è obbligatorio specificare il tipo dell'oggetto --- a meno che il compilatore non sia in grado di determinare il tipo automaticamente --- mentre in quelli di scripting solitamente il tipo è definito a tempo di esecuzione in modo dinamico e la robustezza dei programmi diminuisce in favore della semplicità.

In Lua non esiste la parola chiave new che istanzia il nuovo oggetto in memoria chiamandone il costruttore che risulta essere un metodo qualsiasi dell'oggetto --- addirittura non obbligatorio --- che lo sviluppatore della libreria potrebbe chiamare in qualsiasi modo mentre per esempio in Java esso deve assumere il nome della classe.

Il linguaggio Lua non è quindi progettato con gli stessi obiettivi di Java: non possiede un controllo preventivo del tipo --- i relativi errori di programmazione emergono solamente nella fase di esecuzione --- non prevede una chiara e semplice modalità di generazione dell'oggetto sfruttando funzionalità tortuose poco eleganti e non impone una struttura sintattica unica ma lascia al programmatore la libertà --- e la responsabilità che ne deriva --- di sviluppare soluzioni sintattiche alternative.

I linguaggi oop prevedono la possibilità di dichiarare come privati campi e metodi. Lua non offre invece alcun meccanismo di protezione semplicemente contando sul comportamento corretto dell'utente della libreria.

Tuttavia Lua offre pieno supporto ai principi del paradigma a oggetti senza perdere le caratteristiche generali di un linguaggio semplice.

Una classe 'Rettangolo'


Costruiremo una classe per rappresentare un rettangolo. Si tratta di un ente geometrico definito da due soli parametri: larghezza e altezza.

Un primo tentativo potrebbe essere questo:
-- prima tentativo di implementazione
-- di una classe rettangolo
Rectangle = {} -- creazione tabella (oggetto)

-- creazione di due campi
Rectangle.width = 12
Rectangle.height = 7

-- un primo metodo assegnato direttamente
-- ad un campo della tabella
function Rectangle.area ()
   -- accesso alla variabile 'Rectangle'
   return Rectangle.larghezza * Rectangle.altezza
end

-- primo test
print(Rectangle.area())   --> stampa 84, OK
print(Rectangle.height)   --> stampa  7, OK

Ci accorgiamo presto che questa implementazione basata sulle tabelle è difettosa in quanto non rispetta l'indipendenza degli oggetti rispetto al loro nome e infatti il prossimo test fallisce:
-- ancora la prima implementazione
Rectangle = {width = 12, height = 7}

-- un metodo dell'oggetto
function Rectangle.area ()
   -- accesso alla variabile 'Rectangle' attenzione!
   local l = Rectangle.larghezza
   local a = Rectangle.altezza
   return l * a
end

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

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

Il problema sta nel fatto che nel metodo 'area()' compare il particolare riferimento alla tabella 'Rectangle' che invece deve poter essere qualunque. La soluzione non può che essere l'introduzione del riferimento all'oggetto come parametro esplicito nel metodo stesso, ed è la stessa utilizzata --- in modo nascosto ma vedremo che è possibile nascondere il riferimento anche in Lua --- dagli altri linguaggi di programmazione che supportano gli oggetti.

Secondo quest'idea dovremo riscrivere il metodo 'area()' in questo modo (in Lua il riferimento esplicito all'oggetto deve chiamarsi self pertanto abituiamoci fin dall'inizio a questa convenzione così da poter generalizzare la validità del codice):
-- seconda tentativo
Rettangolo = {larghezza=12, altezza=7}

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

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

print(myrect.larghezza)    --> stampa 12, OK
print(myrect.area(myrect)) --> stampa 84, OK
                           --      funziona!

Fino a ora abbiamo costruito l'oggetto sfruttando le caratteristiche della tabella e la particolarità che consente di assegnare una funzione ad una variabile. Da questo momento entra in scena l'operatore ':' nella chiamata di funzione. Si tratta di zucchero sintattico ovvero una sorta di aiuto fornito dal compilatore in questo caso per rendere il passaggio del riferimento.

L'operatore ':' fa in modo che le seguenti due espressioni siano perfettamente equivalenti anche se le rende differenti dal punto di vista concettuale agli occhi del programmatore:
-- forma classica:
myrect.area(myrect)

-- forma implicita
-- (self prende lo stesso riferimento di myrect)
myrect:area()

Questo operatore è il primo nuovo elemento di Lua introdotto per supportare la programmazione orientata agli oggetti. Se si accede a un metodo memorizzato in una tabella con l'operatore due punti ':' anziché con l'operatore '.' l'interprete Lua aggiungerà implicitamente un primo parametro con il riferimento alla tabella stessa a cui assegnerà il nome di 'self'.

Metatabelle


Il linguaggio Lua si fonda sull'essenzialità tanto che supporta la programmazione ad oggetti utilizzando quasi esclusivamente le proprie risorse di base senza introdurre nessun nuovo costrutto. In particolare Lua implementa gli oggetti utilizzando la tabella l'unica struttura dati disponibile nel linguaggio, e particolari funzionalità dette metatabelle e metametodi.

Il salto definitivo nella programmazione \textsc{oop} consiste nel poter costruire una \emph{classe} senza ogni volta assemblare i campi ed i metodi ma introducendo un qualcosa che faccia da stampo per gli oggetti.

Abbiamo fatto cenno a Java in cui esiste la parola chiave \texttt{class} per la definizione di nuove classi, ma in Lua, dove la semplicità è sempre importante e dove devono continuare ad essere supportati più stili di programmazione, non esistono né nuove parole chiave né una sintassi specifica.

In Lua l'unico meccanismo disponibile per compiere questo ultimo importante passo consiste nelle metatabelle, normali tabelle contenenti funzioni dai nomi prestabiliti che vengono chiamate quando si verificano particolari eventi come l'esecuzione di un'espressione di somma tra due tabelle con l'operatore '+'. Ogni tabella può essere associata a una metatabella e questo consente di creare degli insiemi di tabelle che condividono una stessa aritmetica.

I nomi di queste funzioni particolari dette metametodi iniziano tutti con un doppio trattino basso, per esempio nel caso della somma sarà richiesta la funzione __add() della metatabella associata alle due tabelle addendo --- e se non esiste verrà generato un errore.

Il metametodo più semplice di tutti è __tostring(). Esso viene invocato se una tabella è data come argomento alla funzione 'print()' per ottenere il valore stringa effettivo da stampare.
Se non esiste una metatabella associata con questo metametodo verrà stampato l'indirizzo di memoria della variabile:
-- un numero complesso
complex = {real = 4, imag = -9}
print(complex) --> stampa: 'table: 0x9eb65a8'

-- un qualcosa di più utile: metatabella in sintassi
-- anonima con il metametodo __tostring()
mt = {}
mt.__tostring = function (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

-- assegnazione della metatabella mt a complex
setmetatable(complex, mt)

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

Il metametodo '__index'


Il metametodo che interessa la programmazione a oggetti in Lua è '__index'. Esso interviene quando viene chiamato un campo di una tabella che non esiste e che normalmente restituirebbe il valore 'nil'. Un esempio di codice chiarirà il meccanismo:
-- una tabella con un campo 'a'
-- ma senza un campo 'b'
t = {a = 'Campo A'}

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

-- con metatabella e metametodo
mt = {}
mt.__index = function ()
  return 'Attenzione: campo inesistente!'
end

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

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

Tornando all'oggetto 'Rettangolo' riscriviamo il codice creando adesso una tabella che assumerà il ruolo concettuale di una vera e propria classe:
-- una nuova classe Rettangolo (campi):
Rettangolo = {larghezza=10, altezza=10}

-- un metodo:
function Rettangolo:area()
  return self.larghezza * self.altezza
end

-- creazione metametodo
Rettangolo.__index = Rettangolo

-- un nuovo oggetto Rettangolo
r = {}
setmetatable(r, Rettangolo)

print( r.larghezza ) --> stampa 10, Ok
print( r:area() )    --> stampa 100, Ok

Queste poche righe di codice racchiudono il meccanismo un po' tortuoso della creazione di una nuova classe in Lua: abbiamo infatti assegnato a una nuova tabella 'r' la metatabella con funzione di classe Rettangolo. Quando viene richiesta la stampa del campo 'larghezza' poiché tale campo non esiste nella tabella vuota 'r' verrà eseguito il metametodo '__index' nella metatabella associata che è appunto la tabella 'Rettangolo'.

A questo punto il metametodo restituisce semplicemente la tabella 'Rettangolo' stessa e questo fa sì che divengano disponibili tutti i campi e i metodi in essa contenuti. Il campo 'larghezza' e il metodo 'area()' del nuovo oggetto 'r' sono in realtà quelli definiti nella tabella 'Rettangolo'.

Se volessimo creare invece un rettangolo assegnando direttamente la dimensione dei lati dovremo semplicemente crearli con i nomi previsti dalla classe: 'larghezza' e 'lunghezza'. Il metodo 'area()' sarà ancora caricato dalla tabella 'Rettangolo' ma i campi numerici con le nuove misure dei lati saranno quelli interni dell'oggetto 'r' poiché semplicemente esistono e perciò non sarà presa in considerazione la metatabella. Questa costruzione funziona perfettamente ma lascia al programmatore una situazione di scomodità che deriva dal progetto stesso di Lua e che può essere in parte sanata con l'introduzione del costrutture come vedremo meglio poi.

Le chiamate alle metatabelle e ai metametodi complicano la comprensione del funzionamento del meccanismo di stampa, compito della classe. Può sembrare che ciò influisca negativamente nella scrittura di programmi ad oggetti in Lua. Tuttavia quello che è importante è comprendere il meccanismo delle metatabelle svolto dietro le quinte, poiché in fase di utilizzo degli oggetti, il linguaggio ci apparirà concettualmente simile a una classe.


Il costruttore


Le cose da ricordare di scrivere nel codice sono l'impostazione del metametodo '__index' con la tabella di classe e l'esecuzione della funzione interna 'setmetatable()' per impostare la tabella dell'oggetto stessa come metatabella.

Riproponendo ancora il problema di rappresentare il concetto di rettangolo completiamo il quadro introducendo quello che adesso ci appare come un normale metodo ma che concettualmente assumerà il ruolo di costrutture della classe che chiameremo con il nome convenzionale 'new()'. Il lavoro che dovrà svolgere sarà quello di inizializzare i campi argomento in una delle tante modalità possibili una volta effettuato l'eventuale controllo di validità degli argomenti.

Il codice completo della classe 'Rettangolo' è il seguente:
-- nuova classe Rettangolo (campi con valore di default)
Rettangolo = {larghezza = 1, altezza = 1}

-- metametodo
Rettangolo.__index = Rettangolo

-- metodo di classe
function Rettangolo:area()
  return self.larghezza * self.altezza
end

-- costruttore di classe
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

-- codice utente ------------------
r = Rettangolo:new{larghezza=12, altezza=2}

print(r.larghezza)  --> stampa  12, Ok
print(r:area())     --> stampa  24, Ok

q = Rettangolo:new{larghezza=12}
print(q:area())     --> stampa 12, Ok

Il costruttore accetta una tabella come argomento, altrimenti ne crea una vuota e la restituisce non appena ne ha assegnato la metatabella. In questo modo una tabella qualsiasi entra a far parte della famiglia 'Rettangolo'.
Il costruttore passa al metodo 'new()' il riferimento implicito a 'Rettangolo' grazie all'uso dell'operatore ':' per cui 'self' punterà a 'Rettangolo'.

Quando viene passata una tabella con uno o due campi sulle misure dei lati al costruttore, l'oggetto disporrà delle misure come valori interni effettivi, cioè dei parametri indipendenti che costituiscono il suo stato interno.
Lo sviluppatore può fare anche una diversa scelta, quella per esempio di considerare la tabella argomento del costruttore come semplice struttura di chiavi/valori da sottoporre al controllo di validità e poi includere in una nuova tabella con modalità e nomi che riguardano solo l'implemenazione interna della classe.

Questa volta un cerchio


Per capire ancor meglio i dettagli e renderci conto di come funziona il meccanismo nascosto e automatico delle metatabelle, costruiamo una classe 'Cerchio' che annoveri fra i suoi metodi uno che modifichi il valore del raggio aggiungendo una misura specificata:
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 = 'Sono un cerchio di raggio %0.2f.'
   return string.format(frmt, self.radius)
end

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

   if r then
      o.radius = r
   end

   setmetatable(o, self)
   return o
end

-- codice utente ----------------------
o = Cerchio:new()
print(o)  --> stampa 'Sono un cerchio di raggio 0.00'

o:addToRadius(12.342)

print(o)  --> stampa 'Sono un cerchio di raggio 12.34'
print(o:area())  --> stampa '478.54298786'

Nella sezione del codice utente viene dapprima creato un cerchio senza fornire alcun valore per il raggio. Ciò significa che quando stampiamo il valore del raggio con la successiva istruzione otteniamo 0 che è il valore di default del raggio dell'oggetto 'Cerchio' per effetto della chiamata a '__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.342)'?

Il metodo 'addToRadius()' contiene una sola espressione. Come da regola viene prima valutata la parte a destra ovvero 'self.radius + v'. Il primo termine assume il valore previsto in 'Cerchio' --- quindi zero --- grazie al metametodo, e successivamente il risultato della somma uguale all'argomento 'v' è memorizzato nel campo 'o.radius' che viene creato effettivamente solo in quel momento per poi eventualmente venir utilizzato successivamente in lettura o scrittura.

Ereditarietà

Il concetto di ereditarietà nella programmazione a oggetti consiste nella possibilità di derivare una classe da un'altra per specializzarla.

L'operazione di derivazione incorpora automaticamente nella sottoclasse tutti i campi e i metodi della classe base. Dopodiché si implementano o si modificano i metodi della classe derivata creando una gerarchia di oggetti.

In Lua l'operazione di derivazione consiste nel creare un oggetto con il costruttore della classe base. Si ottiene un riferimento a una tabella che potrà essere popolato di nuovi metodi o campi. Vediamo un esempio semplice:
-- classe base
Sportivo = {}

-- costructor
function Sportivo:new(t)
    t = t or {}
    setmetatable(t, self)
    self.__index = self
    return t
end

-- base methods
function Sportivo:set_name(name)
    self.name = name
end

function Sportivo:print()
    print("'"..self.name.."'")
end

-- derivazione
Schermista = Sportivo:new()

-- specializzazione classe derivata
-- nuovo campo
Schermista.rank = 0

function Schermista:add_to_rank(points)
    self.rank = self.rank + (points or 0)
end

function Schermista:set_weapon(w)
    self.weapon = w or ""
end

-- overriding method
function Schermista:print()
    local fmt = "'%s' weapon->'%s' rank->%d"
    print(string.format(fmt,
        self.name,
        self.weapon,
        self.rank
        ))
end

-- test
s = Sportivo:new{name="Gianni"}
s:print() --> stampa 'Gianni' OK

-- il metodo costruttore new() è quella della classe base!
f = Schermista:new{name="Fencing tiger", weapon="Foil"}
f:add_to_rank(45)

f:print() --> stampa 'Fencing tiger' weapon->'Foil' rank->45

-- chiamata a un metodo della classe base
f:set_name("Amedeo")
f:print() --> stampa 'Amedeo' weapon->'Foil' rank->45

Continua tutto a funzionare per via della ricerca effettuata dal metametodo '__index' che funziona a ritroso fino alla classe base se viene chiamato un campo che non si trova ne nella tabella dell'oggetto ne nella tabella della classe derivata.

E ora gli esercizi...


1 - Aggiungere alla classe 'Rettangolo' vista nel testo il metodo 'print()' che stampi in console con la AsciiArt il rettangolo dalle dimensioni corrispondenti a altezza e larghezza con la corrispondenza 1 unità = 1 carattere.
Si usi il carattere '+' per gli spigoli e i caratteri '-' e '|' per disegnare i lati. Utilizzarre le funzioni di libreria string.rep() e string.format().

2 - Creare una classe corrispondente al concetto di numero complesso e implementare le quattro operazioni tramite metodi (riferimento matematico qui). Aggiungere anche il metodo print() per stampare il numero complesso e poter controllare i risultati di operazioni di test.

3 - ideare una classe base e una classe derivata dandone un'implementazione.

Riassunto della puntata

Abbiamo affrontato la programmazione a oggetti in Lua presentando l'operatore ':' e la tecnologia delle metatabelle e dei metametodi che rendono le usuali tabelle di Lua vere e proprie classi.

Implementare la propria libreria Lua con il paradigma della programmazione a oggetti offre sia allo sviluppatore sia all'utente una maggiore intuitività del linguaggio.

La prossima volta, per l'ultima puntata :-( ci dedicheremo a esempi applicativi.