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.

domenica 26 aprile 2015

Lubit e i pacchetti magici.




Ho sempre odiato la definizione "mago" associato all' informatica, non c'è proprio niente di magico in una materia scientifica se non la curiosità che ci spinge a studiare e scoprire sempre nuovi argomenti... eppure in questo articolo vedremo come fare una cosa che un pò si avvicina ad una piccola magia ;)

Mettiamo di avere un PC  collegato in rete, e che questo PC sia spento quando invece ci serve che sia in funzione perché magari è un qualche tipo di server personale, come possiamo fare?

Beh la risposta più ovvia è andare fisicamente davanti al PC e accenderlo, ma questo sarebbe troppo semplice e di certo non ci sarebbe motivo di scriverci un articolo.

La piccola magia si chiama WOL (Wake on Lan) e  consiste proprio in questo, cioè avviare da remoto un PC collegato ad una rete e raggiungibile.

Un PC che supporta il WOL non è mai completamente spento ma resta costantemente in ascolto in attesa del segnale di richiesta avvio, questo segnale che ci permette di dire al PC che deve "svegliarsi" si chiama Magic Packet , si ... proprio pacchetto magico.

Come già detto di magico non c'è molto, si tratta di un particolare pacchetto ethernet a livello datalink inviato in  broadcast sulla rete LAN con il MAC address della macchina da avviare,  basta conoscere l' indirizzo MAC della scheda di rete del pc da avviare e inviargli un Magic Packet.

Per realizzare quanto detto passiamo al terminale.

Iniziamo con la configurazione del PC da "svegliare"

Prima di tutto dobbiamo assicurarci che la funzione WOL sia supportata dal pc e che sia attivata a livello BIOS, quindi andiamo nella configurazione iniziale del computer e cerchiamo la voce "Wake on lan" o "Wake on PCI" e abilitiamo .

Ora dobbiamo verificare che anche a livello di sistema operativo sia tutto attivato , quindi avviamo il PC e apriamo una finestra terminale, digitiamo :


sudo apt-get install ethtool

sudo ethtool eth0

in questo esempio ho utilizzato l'interfaccia di rete eth0, andrà bene nella maggior parte dei casi, voi potete modificarla con l'identificativo della NIC che utilizzate.

A questo punto se come output riceviamo qualcosa di simile a

......
Wake-on: g
...

vuol dire che il sistema è già predisposto per usare il WOL , se così non fosse dobbiamo attivarlo manualmente, quindi conviene creare un piccolo script da far eseguire automaticamente ad ogni avvio di Lubit digitando :


sudo leafpad /etc/init.d/wol.sh



e nell' editor :


#!/bin/bah 

sudo ethtool eth0  

exit

salviamo il file e usciamo da Leafpad.

Ora rendiamo lo script eseguibile e diciamo a Lubit di avviarlo :


sudo chmod a+x /etc/init.d/wol.sh

sudo update-rc.d wol.sh defaults



La configurazione è terminata, ora dobbiamo annotarci l'indirizzo MAC dell' interfaccia di rete :


ifconfig
e in corrispondenza dell'identificativo eth0, accanto a IndirizzoHW, troviamo il MAC , lo riconoscete perchè è composto da 6 cifre esadecimali separate da due punti , per es. 00:23:54:93:9f:fd .


Fatto questo passiamo al PC che deve inviare il Magic Packet, per farlo esistono molti programmi su qualsiasi sistema operativo, qui useremo gwakeonlan che trovate già disponibile nei repository di sistema.

Installamolo :


sudo apt-get install gwakeonlan



dopo averlo avviato clicchiamo sull'icona "+" per aggiungere i parametri del nostro PC da svegliare


digitiamo un nome di fantasia, l'indirizzo MAC che abbiamo annotato in precedenza e lasciamo il resto inalterato.

A questo punto avremo in elenco il PC appena inserito


 non ci resta che selezionarlo e cliccare su "Accendi" e ...  il gioco è fatto!



Tutto questo funziona a livello MAC, quindi sulla nostra LAN, e se invece il PC da avviare si trova su una LAN diversa?


Beh come avrete notato in fase di inserimento dei paramentri del pc da svegliare non abbiamo modificato gli ultimi  tre parametri, questi ci servono proprio per inviare il pacchetto su una rete diversa dalla nostra LAN.
Naturalmetne la rete dev'essere raggiungibile, quindi se collegata ad internet possiamo spuntare la voce "Internet" , si attiverà il campo "Sistema di destinazione" e inseriamo l'ip pubblico del PC da svegliare, mentre lasciamo inalterata la porta UDP 9.
Naturalmente  per assicurarci che il pacchetto arrivi  a destinazione dobbiamo configurare il router affinchè inoltri al PC i pacchetti UDP ricevuti sulla porta 9.

La pagina del progetto gWakeOnLAN, qui

Alla prossima!

venerdì 24 aprile 2015

Corso Lua - puntata 8 - Iteratori


Iteratori predefiniti per le tabelle

Benvenuti a una nuova puntata del corso base su Lua. Il compito di oggi è spiegare come iterare le tabelle e come costruire nuovi iteratori.

Gli iteratori offrono un approccio semplice e universale per scorrere uno alla volta i valori di una collezione di dati. Vi dedicheremo una puntata proprio perché sono molto utili per scrivere codice efficiente, pulito ed elegante.
Il linguaggio Lua prevede il ciclo generic for per iterare i dati che introduce la nuova parola chiave 'in' secondo questa semantica:
for 'lista variabili' in iterator_function() do
 -- codice
end

Le tabelle di Lua sono oggetti che possono essere impiegati per rappresentare degli array oppure dei dizionari. In entrambe i casi Lua mette a disposizione due iteratori predefiniti tramite le funzioni ipairs() e pairs().

Queste funzioni restituiscono un iteratore che è a sua volta una funzione conforme alle specifiche del 'generic for'. Mentre impareremo più tardi a scrivere iteratori personalizzati, dedicheremo le prossime due sezioni a questi importanti iteratori per le tabelle.

Funzione ipairs()

Questa funzione d'iterazione restituisce a ogni ciclo due valori: l'indice dell'array e il valore corrispondente. Essa comincierà dalla posizione 1 e terminerà quando il valore alla posizione data sarà nil:
-- una tabella array
local t = {45, 56, 89, 12, 0, 2, -98}

-- iterazione tabella come array
for i, n in ipairs(t) do
    print(i, n)
end

Se la tabella contiene valori nil allora l'iteratore non raggiungerà nessuno degli elementi successivi e verranno ignorate tutte le chiavi di tipo non intero.
Il ciclo con ipairs() è equivalente a questo codice:
-- una tabella array
local t = {45, 56, 89, 12, 0, 2, -98}

local i, v = 1, t[1]
while v do
    print(i, v)
    i = i + 1
    v = t[i]
end

Se non interessa il valore dell'indice possiamo convenzionalmente utilizzare per esso il nome di variabile corrispondente a un segno di underscore che in Lua è una definizione valida:
-- una tabella array
local t = {45, 56, 89, 12, 0, 2, -98}

local sum = 0
for _, elem in ipairs(t) do
    sum = sum + elem
end
print(sum)

Se non vogliamo incorrere in errori è molto importante ricordarsi che con ipairs() verranno restituiti i valori in ordine di posizione da 1 in poi solo fino a che non verrà trovato un valore nil. Se ci troveremo in questa situazione dovremo far ricorso all'iteratore pairs() trattato alla prossima sezione.

Funzione pairs()

Questa funzione primitiva di Lua vede la tabella come dizionario pertanto l'iteratore restituirà in un ordine casuale tutte le coppie chiave valore contenute nella tabella stessa.

Una tabella con indici a salti verrà iterata parzialmente da ipairs() ma completamente da pairs():
-- produzione tabella con salto
local t = {45, 56, 89}
local i = 10
for _, v in ipairs({12, 0, 2, -98})  do
    t[i] = v
    i = i + 1
end

print("ipairs() table iteration test")
for index, elem in ipairs(t) do
    print(string.format("t[%2d] = %d", index, elem))
end

print("\npairs() table iteration test")
for key, val in pairs(t) do
    print(string.format("t[%2d] = %d", key, val))
end

Il comportamento di questi due iteratori può lasciare perplessi ma è coerente con un linguaggio di scripting.

Generic for


Come può essere implementato un iteratore in Lua?
Per iterare è necessario mantenere alcune informazioni essenziali chiamate stato dell'iteratore. Per esempio l'indice a cui siamo arrivati nell'iterazione di una tabella/array e la tabella stessa.

Perchè non utilizzare la closure per memorizzare lo stato dell'iteratore?

Abbiamo incontrato le closure nella puntata 6 del corso base su Lua, post che vi invito a rileggere se non vi ricordate o non sapete di cosa si tratta. E allora proviamo a scrivere il codice per iterare una tabella:
-- costructor
local t = {45, 87, 98, 10, 16}

function iter(t)
    local i = 0
    return function ()
        i = i + 1
        return t[i]
    end
end

-- utilizzo
local iter_test = iter(t)
while true do
    local val = iter_test()
    if val == nil then
        break
    end
    print(val)
end

Ok. Funziona, molto semplicemente. Non è stato necessario introdurre nessun nuovo elemento al linguaggio. L'iteratore è solamente una questione d'implementazione che tra l'altro ricrea l'iteratore ipairs() visto poco fa.

Infatti, la funzione iter_test() mantiene nella closure lo stato dell'iteratore e restituisce uno dopo l'altro gli elementi della tabella. Il ciclo infinito --- il while loop --- s'interrompe quando il valore è nil.

Tuttavia data l'importanza degli iteratori Lua introduce un nuovo costrutto chiamato 'generic for' che si aspetta una funzione proprio come la iter() del codice precedente ed effettua i controlli necessari. Vediamo se funziona:
-- costructor
local t = {45, 87, 98, 10, 16}

function iter(t)
    local i = 0
    return function ()
        i = i + 1
        return t[i]
    end
end

-- utilizzo con il generic for
for v in iter(t) do
    print(v)
end

Riassumendo, la costruzione di un iteratore in Lua si basa sulla creazione di una funzione che restituisce uno alla volta gli elementi dell’insieme nella sequenza desiderata. Una volta costruito l’iteratore, questo potrà essere impiegato in un ciclo che in Lua viene chiamato 'generic for'.

Se per esempio si volesse iterare la collezione dei numeri pari compresi nell’intervallo da 1 a 10, avendo a disposizione l’apposito iteratore evenNum(first, last) che definiremo in seguito, potrei scrivere semplicemente:
for n in evenNum(1,10) do
   print(n)
end

L'esempio dei numeri pari

Per definire questo iteratore dobbiamo creare una funzione che restituisce a sua volta una funzione in grado di generare la sequenza dei numeri pari. L’iterazione termina quando giunti all’ultimo elemento, la funzione restituirà il valore nullo ovvero ‘nil’, cosa che succede in automatico senza dover esplicitare un’istruzione di return grazie al funzionamento del 'generic for'.

Potremo fare così: dato il numero iniziale per prima cosa potremo calcolare il numero pari successivo usando la funzione della libreria standard di Lua math.ceil() che fornisce il numero arrotondato al primo intero superiore dell’argomento.
Poi potremo creare la funzione di iterazione in sintassi anonima che prima incrementa di 2 il numero pari precedente --- ed ecco perché dovremo inizialmente sottrarre la stessa quantità all’indice --- e, se questo è inferiore all’estremo superiore dell’intervallo ritornerà l’indice e il numero pari della sequenza. Ecco il codice completo:
-- iteratore dei numeri pari compresi
-- nell'intervallo [first, last]
function evenNum(first, last)
   -- primo numero pari della sequenza
   local val = 2*math.ceil(first/2) - 2
   local i = 0
   return function ()
             i = i + 1
             val = val + 2
             if val<=last then
                return i, val -- due variabili di ciclo
             end
          end
end

-- ciclo con due variabili di ciclo
for idx, val in evenNum(13,20) do
    print(idx, val)
end

In questo esempio, oltre ad approfondire il concetto di iterazione basata sulla closure di Lua, possiamo notare che il 'generic for' effettua correttamente anche l'assegnazione a più variabili di ciclo con regole viste nella puntata del corso in cui abbiamo parlato dell'argomento (la puntata 2).

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

Stateless iterator


Una seconda versione del generatore di numeri pari può essere un buon esempio di un iteratore in Lua che non necessita di una closure per un risultato ancora più efficiente.

Per capire come ciò sia possibile dobbiamo conoscere nel dettaglio come funziona il 'generic for' in Lua; dopo la parola chiave 'in' esso si aspetta tre parametri: la funzione dell’iteratore da chiamare a ogni ciclo, una variabile che rappresenta lo stato invariante e la variabile di controllo.

Nel seguente codice la funzione evenNum() provvede a restituire i tre parametri necessari: la funzione nextEven() come iteratore, lo stato invariante che per noi è il numero a cui la sequenza dovrà fermarsi e la variabile di controllo che è proprio il valore nella sequenza dei numeri pari, e con ciò abbiamo realizzato un stateless iterator in Lua.
La funzione iteratrice nextEven() verrà chiamata a ogni ciclo con nell’ordine lo stato invariante e la variabile di controllo, pertanto fate attenzione, dovete mettere in questo stesso ordine gli argomenti nella definizione:
-- even numbers stateless iterator
local function nextEven(last, i)
   i = i + 2
   if i<=last then
      return i
   end
end
 
local function evenNum(a, b)
   a = 2*math.ceil(a/2)
   return nextEven, b, a-2
end
 
-- example of the 'generic for' cycle
for n in evenNum(10, 20) do
    print(n)
end

E ora gli esercizi...

1 - Dopo aver definito una tabella con chiavi e valori stampare le singole coppie tramite l'iteratore predefinito 'pairs()'.

2 - Scrivere una funzione che accetta una tabella/array di stringhe e utilizzare la funzione di libreria 'string.upper()' per restituire una tabella/array con il testo trasformato in maiuscolo (per esempio con '{"abc", "def", "ghi"}' in '{"ABC", "DEF", "GHI"}').

3 - Scrivere la funzione/closure per l'iteratore che restituisce la sequenza dei quadrati dei numeri naturali a partire da 1 fino a un valore dato.

4 - Scrivere la versione 'stateless' per l'iteratore dell'esercizio precedente.

Riassunto della puntata

Con gli iteratori abbiamo terminato l'esplorazione di base del linguaggio Lua.
Queste prime otto puntate pubblicate sul blog di Luigi sono sufficienti per scrivere programmi utili in Lua perché trattano con essenzialità di tutti gli argomenti necessari.

Il programma del corso prosegue con la prossima puntata che sarà dedicata alla programmazione a oggetti in Lua. State pronti.
R.

sabato 18 aprile 2015

Corso Lua - puntata 7 - La libreria standard


La libreria standard di Lua


Eccoci giunti al grande capitolo della libreria standard di Lua. Si tratta di una collezione di funzioni utili a svolgere compiti ricorrenti su stringhe, file, tabelle, ecc, e si trovano precaricate in una serie di tabelle e quindi immediatamente utilizzabili.
L'elenco completo ma in ordine sparso con il nome della tabella/modulo contenitore e la descrizione applicativa è il seguente:
  • math: matematica;
  • table: utilità sulle tabelle;
  • string: ricerca, sostituzione e pattern matching;
  • io: input/output facility, operazioni sui file;
  • bit32: operazioni bitwise (solo in Lua 5.2)
  • os: date e chiamate di sistema;
  • coroutine: creazione e controllo delle coroutine;
  • utf8: utilità per testo in codifica Unicode UTF-8 (da Lua 5.3)
  • package: caricamento di librerie esterne;
  • debug: accesso alle variabili e performance assessment.

La pagina web a questo indirizzo fornisce tutte le informazioni di dettaglio sulla libreria standard di Lua 5.2.

Libreria matematica

Nella libreria memorizzata nella tabella 'math' ci sono le funzioni trigonometriche 'sin', 'cos', 'tan', 'asin' eccetera --- che come di consueto lavorano in radianti, le funzioni esponenziali 'exp', 'log', 'log10', quelle di arrotondamento 'ceil', 'floor' e quelle per la generazione pseudocasuale di numeri come 'random', 'randomseed', eccetera. Oltre alle funzioni citate la tabella include campi numerici come la costante π.

Un esempio introduttivo:
print(math.pi)
print(math.sin( math.pi/2 ))
print(math.cos(0))

-- accorciamo i nomi delle funzioni ;-)
local pi, sin, cos = math.pi, math.sin, math.cos
local square = function (x) return x*x end
local function one(a)
    return square(sin(a))+square(cos(a))
end

for i=0, 1, 0.1 do
    print(i, one(i))
end

Notate come nella funzione one() entra in azione la closure --- introdotta nella puntata precedente --- perché in essa si fa uso di funzioni definite nell'ambiente esterno rispetto al corpo della funzione stessa. Ciò conferma quanto sia naturale utilizzare questa potente caratteristica di Lua anche con riferimenti a funzione.

Libreria stringhe

In Lua non è infrequente elaborare grandi porzioni di testo.
La libreria per le stringhe è memorizzata nella tabella 'string' ed è una delle più utili.
Con essa si possono formattare campi e compiere operazioni di ricerca e sostituzione.

string.format()

La funzione più semplice è quella di formattazione string.format(). Essa restituisce una stringa prodotta con il formato fornito come primo argomento riempito con i dati forniti dal secondo argomento in poi.

Il formato è esso stesso specificato come una stringa contenente dei campi creati con il simbolo percentuale e uno specificatore di tipo. Per esempio "%d" indica il formato relativo a un numero intero, dove 'd' sta per digit mentre "%f" indica il segnaposto per un numero decimale --- 'f' sta per float.

I campi formato derivano da quelli della funzione classica di libreria printf() del C. Di seguito un codice di esempio:

-- "%d" means digits
local s1 = string.format("%d", 456.18)
print(s1)
local s01 = string.format("%05d", 456.18)
print(s01)

-- "%f" means float
local num = 123.456
local s2 = string.format("intero: %d decimale: %0.2f", num, num)

-- "%s" means string
local s3 = string.format("s1='%s', s2='%s'", s1, s2)
print(s3)

local s4 = string.format("%24s", "pippo")
print(s4)

Come avete potuto notare nel codice di esempio, è anche possibile fornire un ulteriore specifica di dettaglio tra il '%' e lo specificatore di formato, per esempio per gestire il numero delle cifre decimali.

Pattern

Per elaborare il testo si utilizza di solito una libreria per le espressioni regolari. Lua mette a disposizione alcune funzioni di sostituzione e "pattern matching" meno complete dell'implementazione dello standard POSIX per le espressioni regolari ma molto spesso più semplici da utilizzare.

Esistono due strumenti di base, il primo è il pattern e il secondo è la capture.

Il pattern è una stringa che può contenere campi chiamati classi simili a quelli per la funzione di formato vista in precedenza che stavolta però si riferiscono al singolo carattere, e questa differenza è essenziale.

La funzione di base che accetta pattern è string.match() che restituisce la prima corrispondenza trovata nella stringa primo argomento che corrisponde al pattern dato come secondo argomento.

Possiamo ricercare in un numero di tre cifre all'interno di un testo con il pattern "%d%d%d":
-- semplice pattern in azione
local s = "le prime tre cifre decimali di π = 3,141592654 sono"
local pattern = "%d%d%d"
print(string.match(s, pattern))

Le classi carattere possibili sono le seguenti:
  • . un carattere qualsiasi;
  • %a una lettera;
  • %c un carattere di controllo;
  • %d una cifra;
  • %l una lettera minuscola;
  • %u una lettera maiuscola;
  • %p un carattere di interpunzione;
  • %s un carattere spazio;
  • %w un carattere alfanumerico;
  • %x un carattere esadecimale;
  • %z il carattere rappresentato con il codice 0.

Le classi ammettono quattro modificatori per esprimere le ripetizioni dei caratteri:
  • '+' indica 1 o più ripetizioni;
  • '*' indica 0 o più ripetizioni;
  • '-' come '*' ma nella sequenza più breve;
  • '?' indica 0 o 1 occorrenza;

-- occorrenza di un numero intero
-- come una o più cifre consecutive
print(string.match("l'intero 65 interno", "%d+"))
print(string.match("l'intero 0065 interno", "%d+"))

-- e per estrarre un numero decimale?
-- il punto è una classe così dobbiamo utilizzare
-- la classe %. per ricercare il carattere '.'
print(string.match("num = 45.12 :-)", "%d+%.%d+"))

Capture

Il pattern può essere arricchito per non solo trovare corrispondenze ma per restituirne parti componenti. Questa funzionalità viene chiamata capture e consiste semplicemente nel racchiudere nel pattern tra parentesi tonde le sequenze di caratteri ricercati.

Per esempio per estrarre l'anno di una data nel formato 'dd/mm/yyyy' possiamo usare il pattern con la capture seguente "%d%d/%d%d/(%d%d%d%d)":
-- extract only

local s = "This '10/03/2025' is a future date"
print(string.match(s, "%d%d/%d%d/(%d%d%d%d)"))

Più capture nel pattern daranno altrettanti argomenti multipli di uscita:
-- extract all
local s = "This '10/03/2025' is a future date"
local d, m, y = string.match(s, "(%d+%d?)/(%d%d)/(%d%d%d%d)")
print(d, m, y)

string.gsub()

Abbiamo appena cominciato a scoprire le funzionalità dedicate al testo disponibili nella libreria standard di Lua precaricata nell'interprete.

Diamo solo un altro sguardo alla libreria presentando la funzione string.gsub(). Il suo nome sta per global substitution, ovvero sostituisce tutte le occorrenze in un testo.

Intanto per individuare le occorrenze è naturale pensare di utilizzare un pattern e che sia possibile utilizzare le capture nel testo di sostituzione, per esempio:

local s = "The house is black."
print(string.gsub(s, "black", "red"))
print(string.gsub(s, "(%a)lac(%a)", "%2lac%1"))

Il primo argomento è la stringa da ricercare, il secondo è il pattern e il terzo è il testo di sostituzione dell'occorrenza ma può essere una tabella dove le chiavi saranno corrispondenti al pattern saranno sostituite con i rispettivi valori, oppure anche una funzione che riceverà le catture e calcolerà il testo da sostituire.

Una funzione quindi assai flessibile. Mi viene in mente questo esercizio: moltiplicare di 10 tutti gli interi in una stringa, ed ecco il codice:

local s = "Cose da fare oggi 5, cosa da fare domani 2"
print(string.gsub(s, "%d+", function(n)
    return tonumber(n)*10
end))

A questo punto degli esempi avrete certamente capito che 'gsub()' restituisce anche il numero delle sostituzioni effettuate.

Tutte queste funzioni restituiscono una stringa costruita ex-novo e non modificano la stringa originale di ricerca.

Esercizi


1 - Qual è la differenza tra i campi di formato della funzione string.format() e le classi dei pattern? Quali le somiglianze?

2 - Stampare una data nel formato 'dd/mm/yyyy' a partire dagli interi contenuti nelle variabili d, m e y.

3 - Cosa restituisce l'esecuzione della funzione
string.match("num = .123456 :-)", "%d+%.%d+")

Quale pattern corrisponde a un numero decimale la cui parte intera può essere omessa?

4 - Come estrarre dal nome di un file l'estensione?

5 - Come eliminare da un testo eventuali caratteri spazio iniziali e/o finali?

6 - Il pattern "(%d+)/(%d+)/(%d+)" è adatto per catturare giorno, mese e anno di una data presente in una stringa nel formato 'dd/mm/yyyy'?

7 - Creare un esempio che utilizzi la funzione string.gsub() e una funzione in sintassi anonima a due argomenti corrispondenti a due capture nel pattern di ricerca.


Riassunto della puntata

In Lua sono immediatamente disponibili un bel gruppo di funzioni che ne formano la libreria standard. In questa puntata abbiamo esplorato in particolare la libreria dedicata al testo lasciando all'utente di completare l'argomento sui testi di riferimento del linguaggio.

La prossima puntata sarà dedicata al generic for il costrutto di Lua per iterare collezioni di dati.

venerdì 10 aprile 2015

Corso Lua - puntata 6 - Le funzioni


Funzioni

Le funzioni in Lua sono il principale mezzo di astrazione e lo strumento base per rendere il codice strutturato. Nelle prime puntate di questo corso base su Lua ne avete sentito la mancanza, dite la verità!

Coerentemente con il resto del linguaggio la sintassi di una funzione comprende due parole chiave che servono per delimitare il blocco di codice contenuto in essa: 'function' ed 'end'. Una funzione può accettare argomenti e può restituire dati tramite la parola chiave 'return'.

Come primo esempio, vi presento una funzione per calcolare l'ennesimo numero della serie di Fibonacci. Un elemento si ottiene sommando i precedenti due elementi avendo posto uguale a 1 i primi due:
function fibonacci(n)
     if n < 2 then
          return 1
     end
     
     local n1, n2 = 1, 1
     for i = 1, n-1 do
          n1, n2 = n2, n1 + n2 -- assegnazione multipla
     end
     return n1
end

print(fibonacci(10)) --> 55

Con le regole dell'assegnazione multipla una funzione può accettare più argomenti. Se essa verrà chiamata con più argomenti rispetto a quelli che essa prevede quelli in eccesso verranno ignorati mentre, viceversa, se gli argomenti sono inferiori a quelli previsti allora a quelli mancanti verrà assegnato il valore nil.
Ma questo vale anche per i dati di ritorno quando la funzione è usata come espressione in un'istruzione di assegnamento. Basta inserire dopo l'istruzione return la lista delle espressioni separate da virgola che saranno valutate e assegnate alle corrispondenti variabili.

Per esempio, potremo modificare la funzione precedente per restituire la somma dei primi n numeri di Fibonacci oltre che solamente l'ennesimo elemento della serie stessa e considerare un valore di default se l'argomento è nil:

function fibonacci(n)
     n = n or 10 -- the default value is 10
     if n == 1 then
          return 1, 1
     end
     
     if n == 2 then
          return 1, 2
     end
     
     local sum = 1
     local n1, n2 = 1, 1
     for i = 1, n-1 do
          n1, n2 = n2, n1 + n2
          sum = sum + n1
     end
     return n1, sum
end

local fib_10, sum_fib_10 = fibonacci()
print(fib_10, sum_fib_10)

Funzioni: valori di prima classe, I

In Lua le funzioni sono un tipo. Possono essere assegnate a una variabile e passate come argomento a una funzione.

Questa proprietà non si trova spesso nei linguaggi di scripting e offre una nuova flessibilità al codice.

Tutte le funzioni sono memorizzate in variabili. Per assegnare direttamente una funzione a una variabile esiste in Lua la sintassi anonima:
local add = function (a, b)
    return a + b
end

print(add(45.4564, 161.486))

Essendo le funzioni valori di prima classe ne consegue che in Lua le funzioni sono oggetti senza nome esattamente come lo sono tipi come i numeri e le stringhe. Inoltre, la sintassi classica di definizione
function variable_name (args)
    -- function body
end
è solo zucchero sintattico perché l'interprete Lua la tradurrà effettivamente e automaticamente nel codice equivalente
variable_name = function (args)
    -- function body
end

Funzioni: valori di prima classe, II


Un esempio di funzione con un argomento funzione è il seguente, una funzione esegue un numero di volte dato la stessa funzione priva di argomenti:
function print_five()
    print(5)
end

function do_many(fn, n)
    for i=1, n or 1 do
        fn()
    end
end

do_many(print_five)
do_many(print_five, 10)

do_many(function () print("---") end, 12)

Molto interessante, anzi senza dubbio fantastico. Nell'ultima riga di codice l'argomento è una funzione definita in sintassi anonima che verrà eseguita 12 volte.

Per prendere confidenza con il concetto di funzioni come valori di prima classe, cambiamo il significato della funzione precostruita in Lua 'print()'. Ecco come:
local println = print
print = function (n)
    println("Argomento funzione -> "..n)
end

print(12)

Tabelle e funzioni

Se una tabella può contenere chiavi con qualsiasi valore allora può contenere anche funzioni!
Le sintassi sono queste --- esplicitate con il codice riportato di seguito:
  1. assegnare la variabile di funzione a una chiave di tabella;
  2. assegnare direttamente la chiave di tabella con la definizione di funzione in sintassi anonima;
  3. usare il costruttore di tabelle per assegnare funzioni in sintassi anonima.

-- primo caso
local function tipo_i()
    -- body
end

local t = {}
t.func_1 = tipo_i

-- secondo caso
local t = {}
t.func_2 = function ()
    -- body
end

-- terzo caso con più di una funzione
local t = {
    func_3_i = function ()
        -- body
    end,
    
    func_3_ii = function ()
        -- body
    end,
    
    func_3_iii = function ()
        -- body
    end,
}

Con questo meccanismo una tabella può svolgere il ruolo di modulo memorizzando funzioni utili a un certo scopo e in effetti la libreria standard di Lua si presenta all'utente proprio in questo modo.

Variadic arguments

Una funzione può ricevere un numero variabile di argomenti rappresentati da tre dot consecutivi '...'. Nel corpo della funzione i tre punti rappresenteranno la lista degli argomenti, dunque possiamo o costruire con essi una tabella oppure effettuare un'assegnazione multipla.

Un esempio è una funzione che restituisce la somma di tutti gli argomenti numerici:
-- per un massimo di 3 argomenti
local function add_three(...)
    local n1, n2, n3 = ...
    return (n1 or 0) + (n2 or 0) + (n3 or 0)
end

-- con tutti gli argomenti
local function add_all(...)
    local t = {...} -- collecting args in a table
    local sum = 0
    for i = 1, #t do
        sum = sum + t[i]
    end
    return sum
end

print(add_three(40, 20))
print(add_all(45, 48, 5456))
print(add_three(14, 15), add_all(-89, 45.6))

Per inciso, anche la funzione base 'print()' accetta un numero variabile di argomenti.
Il meccanismo è ancora più flessibile perché tra i primi argomenti vi possono essere variabili "fisse".
Per esempio il primo parametro potrebbe essere un moltiplicatore:
local function add_and_multiply(molt, ...)
    local t = {...}
    local sum = 0
    for i = 1, #t do
        sum = sum + t[i]
    end
    
    return molt * sum
end

print(add_and_multiply(10, 45.23, 48, 9.36, -8, -56.3))

Un'altra funzione predefinita 'select()' consente di accedere alla lista degli argomenti in dettaglio. Infatti se tra gli argomenti compare un valore nil avremo problemi ad accedere ai paraemetri successivi nel codice precedente perché --- come sappiamo già --- l'operatore di lunghezza # considera il nil come valore sentinella di fine array/tabella.

Il selettore prevede un primo parametro fisso e la lista variabile inserita con i tre punti '...'. Se questo parametro è un intero allora questo verrà utilizzato come indice per restituire il corrispondente parametro. Se invece il parametro è la stringa "#" restitisce il numero di argomenti extra presenti dopo l'eleventuale nil intermedio.
Il codice seguente preso pari pari dal PIL --- certamente il punto di riferimento principale su Lua scritto dallo stesso Autore del linguaggio Roberto Ierusalimschy, tra l'altro composto in LaTeX e venduto come contributo al progetto Lua stesso:
for i = 1, select("#", ...) do
    local arg = select(i, ...)
    -- loop body
end

Omettere le parentesi tonde se...

In Lua esiste la sintassi di chiamata a funzione semplificata, ammessa opzionalmente solo se:
  • la funzione accetta un unico argomento di tipo stringa;
  • la funzione accetta un unico argomento di tipo tabella.
e consiste nella possibilità di ommettere le parentesi tonde ().
Per esempio:

print "si è possibile anche questo..."

local function is_empty(t)
    if #t == 0 then
        return true
    else
        return false
    end
end

-- questo:
print(is_empty{})
print(is_empty{1, 2, 3})

-- invece di questo (sempre possibile):
print(is_empty({}))
print(is_empty({1, 2, 3}))

Closure

Chiudiamo la puntata con uno strano termine forse meglio noto agli sviluppatori dei linguaggi funzionali: la closure.

Questa proprietà di Lua amplia il concetto di funzione rendendo possibile l'accesso dall'interno di essa ai dati presenti nel contesto esterno. Ciò è possibile perché alla chiamata di una funzione viene creato uno spazio di memoria del contesto esterno unico e indipendente.

Tutte le chiamate a una stessa funzione condivideranno una stessa closure.

Se questo è vero una funzione potrebbe incrementare un contatore creato al suo interno, e anche qui prendo l'esempio di codice dal PIL:
local function new_counter()
    local i = 0 -- variabile nel contesto esterno
    return function ()
        i = i + 1 -- accesso alla closure
        return i
    end
end

local c1 = new_counter()
print(c1()) --> 1
print(c1()) --> 2
print(c1()) --> 3
print(c1()) --> 4
print(c1()) --> 5

local c2 = new_counter()
print(c2()) --> 1
print(c2()) --> 2
print(c2()) --> 3

print(c1()) --> 6

A parole il codice definisce una funzione 'new_counter()' che restituisce una funzione che ha accesso indipendente al contesto (la variabile 'i').
Come si verifica dalle righe successive a ogni chiamata della funzione contatore il valore catturato è unico e indipendente.

Tecnicamente la closure è la funzione effettiva mentre invece la funzione non è altro che il prototipo della closure.

Le closure consentono di implementare diverse tecniche utili in modo naturale e concettualmente semplice. Una funzione di ordinamento potrebbe per esempio accettare come parametro una funzione di confronto per stabilire l'ordine tra due elementi tramite l'accesso a una seconda tabella esterna contenente informazioni utili per l'ordinamento stesso.

Nel prossimo esempio mettiamo in pratica l'idea appena presentata. Il codice utilizza una funzione della libreria di Lua, che introdurremo nella prossima puntata, in particolare table.sort(), per applicare l'algoritmo di ordinamento alla tabella passata come argomento in base al criterio di ordine stabilito con la funzione passata come secondo argomento in sintassi anonima.

local years = {1994, 1996, 1998, 2000, 2002}
local val = {
    [1994] = 12.5,
    [1996] = 10.2,
    [1998] = 10.9,
    [2000] =  8.9,
    [2002] = 12.9,
}

local function sort_by_value(tab)
    table.sort(tab,
        function (a, b)
            return val[a] > val[b]
        end
    )
end

sort_by_value(years)

for i = 1, #years do
    print(years[i])
end

Esercizi


1 - Scrivere una funzione che sulla base della stringa in ingresso "+", "-", "*", "/" restituisca la funzione corrispondente per due argomenti.

2 - Scrivere la funzione che accetti due argomenti numerici e ne restituisca i risultati delle quattro operazioni aritmetiche.

3 - Scrivere una funzione che restituisca il fattoriale di un numero memorizzandone in una tabella di closure i risultati per evitare di ripetere il calcolo in chiamate successive con pari argomento.

4 - Scrivere una funzione con un argomento opzionale rispetto al primo parametro numerico che ne restituisca il seno interpretandolo in radianti se l'argomento opzionale è nil oppure "rad", in gradi sessadecimali se "deg" o in gradi centesimali se "grd".

5 - Scrivere una funzione che accetti come primo argomento una funzione f: R -> R (prende un numero e restituisce un numero), come secondo e terzo argomento i due valori dell'intervallo di calcolo e come quarto argomento il numero di punti in cui suddividere l'intervallo. La funzione dovrà stampare i valori che la funzione argomento assume nei punti definiti.

Riassunto della puntata

Una puntata bella densa dedicata alle funzioni di Lua caratterizzate dall'essere valori di prima classe --- o anche valori higher-order --- e dalle closures. Niente male per un linguaggio di scripting ;-)

martedì 7 aprile 2015

Che tipo di triangolo è? [Python3]

Ripropongo lo stesso esercizio fatto con bash qualche giorno fa (qui). Questa volta con Python, però. L'esercizio consiste nello scrivere uno script che, dopo avergli passato tre interi, mi restituisca un tipo di triangolo: equilatero, isoscele o scaleno.

Perchè questi esercizi? Perchè solo scrivendo e leggendo codice si impara a programmare. I manuali stanno alla base, ma non bastano, serve pratica. E molta.

Ok, passiamo allo script di oggi. Se ritenete che qualcosa non vada, scrivetelo pure nei commenti.

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

def tipo_triangolo():
    a=input("inserisci la misura del primo lato: ")
    if a.isdecimal():
        a=int(a)
    else:
        print(a, "non è un intero valido")
        raise SystemExit
    b=input("inserisci la misura del secondo lato: ")
    if b.isdecimal():
        b=int(b)
    else:
        print(b, "non è un intero valido")
        raise SystemExit
    c=input("inserisci la misura del terzo lato: ")
    if c.isdecimal():
        c=int(c)
    else:
        print(c, "non è un intero valido")
        raise SystemExit

    if a+b>c and a+c>b and b+c>a:
        if a==b and b==c:
            print("triangolo equilatero")
        elif a==b or a==c or b==c:
            print("triangolo isoscele")
        else:
            print("triangolo scaleno")
    else:
        print("Non è stata rispettata la disuguaglianza triangolare")

tipo_triangolo()

La funzione input() mi restituisce sempre una stringa, anche quando digito un numero.

Provare per credere.


Bisogna fare in modo di trasformare quella stringa in un intero, qualora volessimo utilizzarlo come intero, ovvio.

Deputato a fare una cosa del genere è int(), ma non basta, serve un passaggio intermedio; infatti bisogna prima controllare se quella stringa possa o meno essere convertita in un intero, a tal fine utilizzeremo la funzione isdecimal().

isdecimal() darà come risultato semplicemente o true o false.

Se il controllo di isdecimal() sarà superato, nel senso che risponderà "true", allora la stringa sarà convertibile in intero, perciò la passo a int(), che la convertirà; diversamente lo script mi dirà che ciò che abbiamo dato in pasto ad input() è un intero non valido e restituirà un exit 1.

La prima parte dello script è volutamente prolissa, avrei potuto abbreviarlo di molto. La sintesi, tuttavia, non è sempre un bene.

Nella seconda parte si decide il tipo del triangolo, ma solo se è stata rispettata la disuguaglianza triangolare, cioè che la somma della misura di due lati sia maggiore della misura del terzo.


Ovviamente potete offrire le vostre soluzioni. A presto :)

domenica 5 aprile 2015

Che tipo di triangolo è? [Brevi Esercizi]

Salve!

È la vigilia di Pasqua, cosa faccio cosa non faccio, alla fine decido di scrivere uno script in bash.

Lo script chiede tre numeri interi, che sarebbero le misure dei lati di un triangolo, e restituisce il tipo del triangolo stesso, equilatero, isoscele, scaleno.

Può sembrare un'operazione semplice, ma in realtà non lo è. E non lo è per tutta una serie di ragioni. Anzitutto perché bash, confrontato con altri linguaggi, come Python ad esempio, non si presta molto a queste cose. Ed è proprio questo il bello. Spingersi oltre per vedere l'effetto che fa.

E poi, quando tutto sembra funzionare, vieni assalito da mille dubbi. E se al posto di un numero intero passi una stringa o un numero pari a zero o addirittura negativo? E poi bastano veramente tre lati per costruire un triangolo? Sì, bastano tre lati, ma non tre lati qualsiasi. Avete mai sentito parlare di disuguaglianza triangolare? La regola della disuguaglianza triangolare dice che ogni lato deve essere inferiore alla somma degli altri due. Vi faccio un esempio. Vi do la misura di tre lati, 1m, 2m, 80m, provate ora a costruire mentalmente un triangolo...Ecco, evidentemente un triangolo consta di tre lati, ma non basta, serve dell'altro.

E se passo allo script un numero decimale? Un casino, dovrei riscrivere alcune parti...magari lo lascio come esercizio, ci sarebbe bc... :)

Con Python è tutto più semplice, con bash mica tanto. Io ci ho provato. Sicuramente l'esercizio è migliorabile, perfezionabile e via dicendo.

#!/bin/bash

read -p "Digita la misura del primo lato: " a
read -p "Digita la misura del secondo lato: " b
read -p "Digita la misura del terzo lato: " c 


for i in $a $b $c; do
      if [ $i -gt 0 ] 2>/dev/null; then
             echo "" 
      else
             echo "devi immettere numeri interi e maggiori di zero" && exit 1
      fi
done


let d=$b+$c
let e=$a+$c
let f=$a+$b

if [[ $a -gt $d || $b -gt $e || $c -gt $f ]]; then
 clear
 echo -e "\nNon è stata rispettata la disuguaglianza triangolare\n" && exit 1
fi       


ordina=$( printf '%s\n' $a $b $c | sort -n )
risultato=$( uniq -c <<< "$ordina" | wc -l )

clear
case $risultato in
    1) echo -e "\nTRIANGOLO EQUILATERO\n" ;;
    2) echo -e "\nTRIANGOLO ISOSCELE\n"   ;;
    3) echo -e "\nTRIANGOLO SCALENO\n"    ;;
esac

Con read -p chiedo i tre lati. Poi il primo controllo. Se digito una stringa al posto di un numero intero oppure, se digito un numero non maggiore di zero, lo script mi ritorna exit 1. Ho usato un ciclo for, e il controllo lo fa l'operatore -gt che accetta solo numeri interi.
Poi il secondo controllo, quello della disuguaglianza triangolare, se ogni lato non è inferiore alla somma degli altri due, lo script mi ritorna un exit 1.

Passato questo secondo controllo, magari ne potremmo aggiungere altri, ma ora non mi viene niente, si passa all'elaborazione dei dati.

Printf mette in colonna i dati e sort -n li ordina. I dati ordinati vengono dati in pasto a uniq -c attraverso il "<<<", che si chiama here strings, e poi passati in input a wc -l.
P

Per capire che fa uniq -c dobbiamo fare un esempio. Poniamo che passi allo script i seguenti dati: 2 1 2

Questi tre dati vengono messi in colonna da printf:

2
1
2

Poi sort -n li ordina, dal più piccolo al più grande:

1
2
2

uniq -c conta quante volte compare un dato, in questo caso:

1 1
2 2

Infine wc -l conta quante righe ci sono. Se le righe sono 3, il triangolo è scaleno, se ce ne sono due, il triangolo è isoscele, se una, è equilatero.

Facciamo un altro esempio. Passo allo script i seguenti dati:  6, 4, 5;

printf li mette in colonna, sort -n li ordina, uniq -c conta le frequenze di ogni numero. In questo caso:

1 4
1 5
1 6

wc -l darà come risultato 3, che significa tre lati disuguali, quindi triangolo scaleno. E sarà il case a darci una risposta.


Alla prossima! E Buona Pasqua.

sabato 4 aprile 2015

Corso Lua - puntata 5 - Operazioni logiche e stringhe


Operatori logici

Gli operatori logici 'and' 'or' e 'not' danno luogo ad alcune espressioni idiomatiche di Lua.
Cominciamo con 'or': è un operatore logico binario. Se il primo operando è vero lo restituisce altrimenti restituisce il secondo.

Ricordatevi che in Lua un'espressione è vera se è il valore 'true' oppure se è un valore non nil, quindi per esempio nel seguente codice 'a' vale sempre 123.

local a = 123 or "mai assegnato"
print(a)


L'operatore 'and' restituisce il primo operando se esso è falso altrimenti restituisce il secondo operando.

Con and e or combinati otteniamo l'operatore ternario del C++ che è "a ? b:c" in Lua: Ecco l'espressione in un esempio di codice: se 'a' è vero restituisce 'b' altrimenti 'c':

local val = (a and b) or c

Poiché 'and' ha priorità maggiore rispetto a 'or' nell'espressione precedente possiamo omettere le parentesi per un codice più idiomatico:

local val = a and b or c


Il massimo tra due numeri è un'espressione condizionale:

local x = 45.69
local y = 564.3

local max
if x > y then
    max = x
else
    max = y
end

ma con gli operatori logici è tutto più Lua...
local x = 45.69
local y = 564.3

local max = (x > y) and x or y

L'operatore logico 'not' restituisce true se l'operando è nil oppure se è false --- valore booleano --- e viceversa restituisce false se l'operando non è nil oppure è true. Alcuni esempi:
print(not 5)       --> 'false'
print(not not 5)   --> 'true'
print(not true)    --> 'false'
print(not false)   --> 'true'
print(not nil)     --> 'true'

L'operatore di negazione può essere usato per controllare se una variabile è valida oppure no. Per esempio possiamo controllare se in una tabella esiste un campo 'prezzo':
local t = {} -- una tabella vuota

if not t.prezzo then -- t.prezzo è nil
    print("assente")
else
    print("presente")
end

t.prezzo = 12.00
if not t.prezzo then
    print("assente")
else
    print("presente")
end

Le stringhe

In Lua le stringhe rappresentano uno dei tipi di base del linguaggio.
Per rappresentare valori stringa letterali ci sono tre diversi delimitatori:
  • doppi apici: carattere ";
  • apice semplice: carattere ';
  • doppie parentesi quadre: delimitatori [[ e ]] con o senza =.

In una stringa delimitata da doppi apici possiamo inserire liberamente apici semplici e viceversa, e caratteri non stampabili come il ritorno a capo (\n) e la tabulazione (\t), tramite il carattere di escape backslash che quindi va inserito come doppio backslash:
local s1 = "doppi 'apici'"
local s2 = 'apici semplici e non "doppi"'
local s3 = "prima riga\nseconda riga"
local s4 = "una \\macro"
local s5 = "\""
assert( s5 == '"')

print(s1)
print(s2)
print(s3)
print(s4)
print(s5)

In Lua non esiste il tipo carattere quindi gli Autori del linguaggio hanno pensato di utilizzare i delimitatori normalmente destinati a rappresentare i char per consentire all'utente di creare stringhe contenenti i delimitatori doppi apici e viceversa, senza utilizzare l'escaping. Infatti i caratteri \" e \' rappresentano comunque i caratteri apici corrispondenti, come si vede nella variabile s5 del codice precedente.

Il terzo tipo di delimitatore per le stringhe è una coppia si parentesi quadre e ha la proprietà di ammettere il ritorno a capo. Si possono così introdurre nel sorgente interi brani di testo nel quale i caratteri di escaping non saranno interpretati.
local long_text = [[
Questo è un testo multiriga
dove i caratteri di escape non contano
come \n o \" o \' o \\.

Inoltre, se il testo come in questo caso
comincia con un ritorno a capo allora questo
carattere \n sarà ignorato.
]]

print(long_text)

Se per caso nel testo fossero presenti i delimitatori di chiusura è possibile inserire un numero qualsiasi di caratteri '=' tra le parentesi quadre, purché il numero sia lo stesso per i delimitatori di apertura e chiusura, esempio:
local long_text = [=[
Questo è il codice Lua da stampare:

local tab = {10, 20, 30}
local idx = {3, 2, 1}
print(tab[idx[1]]) -- notare doppia parentesi quadra chiusa

local long_text = [[ -- questo non potremo farlo...
    Testo lungo...
]]

Tutto chiaro?
In Lua le stringhe letterali nel codice
possono essere proprio letterali
senza caratteri di escape e senza
preoccupazioni sulla presenza di gruppi
di delimitazione di chiusura...
]=]

print(long_text)

Commenti multiriga

Questi delimitatori variabili con numero qualsiasi di segni '=' li troviamo nei commenti multiriga di Lua. Abbiamo incontrato fino a ora i commenti di riga che si introducono nel codice con un doppio trattino '--'.

I commenti multiriga sono comodi quando si vuol escludere dall'esecuzione un intero blocco di righe: iniziano con i doppi trattini seguiti da un delimitatore di stringa multiriga e terminano con la corrispondente chiusura:
-- questo è un commento di riga

--[[
questo è un commento
multiriga
]]

--[=[
e anche questo è un commento
multiriga
]=]

Normalmente in Lua i commenti multiriga vengono chiusi premettendo i doppi trattini anche al gruppo delimitatore di chiusura. Questo è solo un trucco per riattivare rapidamente il codice eventualmente contenuto nel commento, basta uno spazio per far diventare il commento multiriga :
--[[ righe di codice non attive

local tab = {}
--]]

-- [[ notare lo spazio dopo i doppi trattini
-- questo codice invece viene eseguito

local tab = {}
--]] -- e questo diventa una normale riga di commento

L'editor SciTE che vi consiglio per scrivere il codice Lua, è in grado di colorare correttamente il testo dei commenti di riga e di quelli multiriga.

Concatenare stringhe e immutabilità

In Lua l'operatore '..' concatena due stringhe, in questo modo:
local s1 = "Hello" .. " " .. "world"
local s2 = s1 .. " Ok"
s2 = s2 .. "."

print(s1 .. "!")
print(s2)

Il concetto importante riguardo alle stringhe è se queste siano o no immutabili. Se non lo sono la concatenazione di stringhe non comporta la creazione di una nuova stringa risultato dell'operazione. In Lua, come in molti altri linguaggi, le stringhe sono invece immutabili.
Ciò significa che una volta create le stringhe non possono essere modificate e nel codice precedente, l'operazione di concatenare il carattere punto in coda alla stringa 's2', genera una nuova stringa che è assegnata alla stessa variabile.

Per poche operazioni di concatenazione ciò non è un problema, ma in alcuni casi invece si. Consideriamo il seguente codice apparentemente innocuo:
local s = ""

for i = 1, 100 do
    s = s .. "**"
end

print(#s) -- l'operatore length funziona anche per le stringhe!

Ma cosa succede in dettaglio? Perché questo codice non è efficiente?
Ad ogni concatenazione viene creata una nuova stringa. La prima volta vengono copiati due byte per dare la stringa "**". La seconda iterazione la memoria copiata sarà di 4 byte, e alla terza di 6 byte, eccetera.
Ad ogni iterazione la memoria copiata cresce di due byte con il risultato che per produrre una stringa di 200 asterischi (200 byte) avremo copiato in totale la memoria equivalente a 10100 byte!

In Java e negli altri linguaggi con stringhe immutabili normalmente si corre ai ripari mettendo a disposizione una struttura dati o una funzione che risolve il problema, per esempio un tipo StringBuffer. In Lua la soluzione è una funzione della libreria table che, anticipando rispetto alle nostre chiaccherate è table.concat():
local t = {}
for i = 1, 100 do
    t[#t + 1] = "**"
end

print(#table.concat(t))

Per la cronaca, nel caso specifico avremo dovuto usare la funzione string.rep() ma table.concat() è più generale.

Esercizi


1 - Prevedere il risultato delle seguenti espressioni Lua:
local a = 1 or 2
local b = 1 and 2
local c = "text" or 45

local d = not 12 or "ok"
local e = not nil or "ok"

2 - Usando gli operatori logici di Lua codificare l'espressione che restituisce la stringa "grande", "uguale" o "piccolo" a seconda dei valori numerici contenuti in due variabili.

3 - Come fare in Lua per creare una stringa letterale contenente sia il carattere apice semplice che doppio?

4 - Quale sarà il risultato dell'esecuzione del seguente codice?

local s = "'"..'"'.."ok"..[["']]
print(s)

5 - Creare la stringa "\/".

6 - Scrivere un programma che a partire dalla stringa "*" crei e stampi la stringa di 64 asterischi senza utilizzare l'operatore di concatenazione o la funzione string.rep().

7 - Scrivere un programma che a partire dalla stringa "*" crei e stampi la stringa di 64 asterischi usando l'operatore di concatenazione il minimo indispensabile di volte.

Riassunto della puntata

Oggi abbiamo introdotto gli operatori logici, le modalità di inserimento delle stringhe e la loro concatenazione. Una puntata un po' noiosa se vogliamo, ma questi dettagli devono pur essere conosciuti per programmare.

D'altra parte non possiamo --- specie per le stringhe --- studiare esempi significativi fino a che non avremmo introdotto la libreria standard di Lua che a sua volta non possiamo introdurre fino a che non avremmo parlato delle funzioni.

E allora facciamolo, la prossima volta parleremo delle funzioni in Lua.
Alla prossima.

venerdì 3 aprile 2015

Lubit & Cloud

Salve, 
iniziamo con una domanda:

Chi di voi usa, o ha usato almeno una volta, il famoso Dropbox, oppure Box.net, Google drive, skyDrive, solo per citare alcuni tra i più famosi?


Sicuramente molti! 


I servizi di archiviazione cloud disponibili in rete sono ormai davvero tanti e molto utili, alcuni sono anche gratuiti sebbene con dei limiti.
Questi servizi ci permettono, come è noto, di conservare i nostri files in servers remoti, sicuri e veloci ma non di nostra proprietà e con dei limiti di capacità e personalizzazione, almeno nelle versioni gratuite.
A volte, come nel mio caso, capita di utilizzare più servizi avendo così i files sparsi nei vari cloud.
Come fare quindi per avere una soluzione analoga a quelle commerciali, che sia di nostra proprietà dove possiamo implementare tutte le funzionalità che ci servono e magari che faccia anche da aggregatore dei files che abbiamo sparsi in giro nei vari cloud?
Beh, una valida risposta per creare un nostro cloud privato è il software open source OwnCloud.
In questo articolo vedremo come installarlo e configurarlo partendo da zero con solo un sistema linux, naturalmente Lubit :).
La scelta di Lubit linux si addice molto bene al lavoro che andiamo a realizzare perché è un sistema operativo molto leggero , veloce e che necessita di poche risorse di sistema. 

Pronti? Iniziamo! 

Prepariamo l'ambiente server

Owncloud è un applicativo web, funziona quindi come un sito web.
Per quanto riguarda la gestione degli archivi si può scegliere, in fase di installazione, di utilizzare Sqlite, strumento più leggero ma poco adatto a un uso intensivo, oppure si può usare mysql o postgresql.
Per realizzare il nostro cloud personale utilizzeremo il più robusto e affidabile mysql che ci consente di avere una soluzione più adatta anche all' uso da parte molti utenti.
Come primo passo andiamo ad installare il server web apache2, il supporto per php e mysql, per fare ciò con lubit procediamo da riga di comando digitando nel terminale:
 
aggiorniamo il sistema



sudo apt-get update 
sudo apt-get upgrade

installiamo  l’ambiente LAMP


sudo tasksel install lamp-server


ci viene chiesta la password per l’utente root di mysql , la digitiamo , e l’installazione viene completata. (consiglio di annotare la password, servirà più avanti)
A questo punto abbiamo tutto quello che ci serve per poter proseguire con l’installazione di OwnCloud.

Installiamo OwnCloud

 Da terminale digitiamo:

per scaricare  le chiavi: 
 


wget http://download.opensuse.org/repositories/isv:ownCloud:community/xUbuntu_14.10/Release.key


sudo apt-key add - < Release.key

fatto questo aggiungiuamo il repo ufficiale al source.list e aggiorniamo:

sudo sh -c "echo 'deb http://download.opensuse.org/repositories/isv:/ownCloud:/community/xUbuntu_14.10/ /' >> /etc/apt/sources.list.d/owncloud.list"


sudo apt-key add - < Release.key


sudo apt-get update
a questo punto procediamo con l’installazione digitando

sudo apt-get install owncloud

Creiamo il database

Come dicevo all’ inizio abbiamo scelto di usare mysql, quindi dobbiamo creare un database riservato a owncloud.

Da terminale digitiamo:

mysql -u root -p


digitiamo la password di root che abbiamo inserito al momento dell’ installazione.

A questo punto ci troviamo nel prompt di mysql, digitiamo:

create database daticloud;


Creiamo l’utente per il database e diamogli i  privilegi:

grant all privileges on daticloud.* to 'utente'@'localhost' identified by 'password' with grant option;


dove al posto di utente scriviamo il nome utente e in password la password per l’utente creato.

digitiamo quit per uscire da mysql.
Ok adesso siamo pronti ad usare il nostro cloud personale? beh si, ma c’è da tener presente che tutti i dati gestiti dal nostro sistema , fino ad ora, vengono trasmessi “in chiaro” e quindi potrebbero essere intercettati.

Per ovviare al problema procediamo  ad installare il supporto alla cifratura ssl.

Un pò di sicurezza! 

Sempre da terminale digitiamo:

sudo apt-get install openssl
sudo a2enmod ssl
sudo mkdir /etc/apache2/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/ssl/owncloud.key -out /etc/apache2/ssl/owncloud.crt

a questo punto il sistema genera il certificato e le chiavi ssl , per fare questo ci richede delle informazioni.

Riassumendo i campi richiesti (e le risposte suggerite) sono:

Country Name ( IT);
State (Italia);
Locality Name (possiamo lasciare vuoto);
Organization Name (un nome qualsiasi);
Organization Unit Name (lasciamo vuoto);
Common Name (IP pubblico, il nostro dominio dinamico o l’indirizzo web del server);
email (possiamo lasciare vuoto).

ora abilitiamo i certificati appena creati digitando:

sudo leafpad /etc/apache2/sites-available/default-ssl.conf


 modifichiamo le voci come segue:

ServerName (IP-pubblico digitato in Common Name)
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/owncloud.crt
SSLCertificateKeyFile /etc/apache2/ssl/owncloud.key

Se alcune di queste voci non vengono trovate si può tranquillamente digitarle in uno spazio vuoto .

Salviamo il file di configurazione e da terminale digitiamo:


sudo a2ensite default-ssl
sudo service apache2 restart

Finalmente ci siamo.
Adesso siamo pronti per il  primo accesso al nostro cloud e al wizard di configurazione.

Da web browser andiamo all’ indirizzo:

https://localhost/owncloud

Il browser ci avvertirà che il stiamo facendo accesso ad un sito cifrato, clicchiamo su “sono consapevole dei rischi”, poi “aggiungi eccezione”
 



 

seguito da “acquisisci certificati” e “conferma eccezione di sicurezza” . 



Ecco che ci appare la schermata di configurazione di OwnCloud !!!
 


compiliamo il form con i dati richiesti:

nome utente  e  password

clicchiamo su Storage & database quindi scegliamo mysql e nei campi a seguire digitiamo l’utente creato per il database e la sua password,
il nome del database “daticloud” e lasciamo inalterato localhost.



Confermiamo e, se tutto è andato per il verso giusto, in pochi secondi ci troveremo il nostro server cloud perfettamente funzionante.

Per ultimo attiviamo la forzatura del protocollo https  andando nel menu in alto a destra (sul nostro nome utente) e scegliendo “Admin”  spuntiamo la voce “Forza HTTPS”.

Finito! 

Resterebbe da configurare le porte del nostro router per poter accedere al sistema da remoto ma questa è un'altra storia ;)
 
Saluti.