venerdì 27 marzo 2015

Corso Lua - puntata 4


Hello world!

Come tutti i linguaggi di scripting non occorre compilare il codice Lua. Basta scrivere il programma in un file di testo --- preferibilmente assegnandogli l'estensione .lua --- ed eseguirlo con il comando da terminale $ lua nomefile.

Per scrivere il codice vi consiglio di utilizzare l'editor SciTE perché è già predisposto per eseguire programmi Lua ed è programmabile in Lua. Tra l'altro l'output del programma viene visualizzato nella output tab che funge da comodo terminale. Il tasto funzione F5 esegue il programma visualizzato nella scheda attiva in quel momento e il tasto F8 attiva/diasttiva la finestra di output che è un terminale incorporato nell'editor.

Prepariamoci a scrivere il primo programma in Lua, ovviamente il fatidico 'Hello world!'.
Salviamo il seguente codice in un file chiamato "primo.lua" ed eseguiamolo direttamente in SciTE premendo il tasto funzione F5 o al terminale con il comando $ lua primo.lua:

  print("Hello world!")


In ambiente Linux o Mac OS X come per tutti gli altri linguaggi di scripting possiamo inserire come prima riga la sha-bang. Dando i permessi di esecuzione al file possiamo eseguire il programma semplicemente scrivendone il nome al terminale, e in questo caso è conveniente omettere l'estensione.
Ecco il programma:
#!/usr/bin/env lua

print("Hello world!")

Lua è in grado di passare al programma gli argomenti che l'utente digita separandoli da spazi nella linea di comando, tramite la tabella array chiamata 'arg'. arg[0] conterrà il nome dello script mentre arg[1], arg[2], eccetera conterrà i vari argomenti utente convertiti nel tipo stringa. Questa proprietà assieme alla tecnica dello sha-bang da all'utente la possibilità di scrivere programmi in Lua come se fossero comandi da terminale, almeno per gli ambienti di classe *nix.

Primo problema: il ciclo for

Va bene, ma siamo qui per scrivere codice e allora cominciamo con il voler contare i numeri pari contenuti in una tabella che funziona come un array --- ricordandoci che gli indici partono da 1 e non da 0.
Rileggete le prime tre puntate del corso come utile riferimento.

Cominciamo creando la tabella con il costruttore in linea per poi iterare con un ciclo for.
-- costruttore (l'ultima virgola è opzionale)
-- i doppi trattini rappresentano un commento
-- come // lo sono per il C
local t = {12, 45, 67, 101,   3,
            2, 89, 36,   7,  99,
           88, 33, 17,  12, 203,
           46,  1, 19,  50, 456}

local c = 0 -- contatore
for i = 1, #t do
    if t[i] % 2 == 0 then
        c = c + 1
    end
end

print(c)

Il corpo del ciclo 'for' di Lua è il blocco compreso tra le parole chiave obbligatorie 'do' ed 'end'. La variabile 'i' interna al ciclo assumerà i valori da 1 al numero di elementi della tabella ottenuti con l'operatore #.

Per ciascuna iterazione con il costrutto condizionale 'if' incrementeremo un contatore solo nel caso che l'elemento della tabella è pari. L'if ha anch'esso bisogno di definire il blocco e lo fa con le parole chiavi obbligatorie 'then' e 'end'.

Ma come si comporta l'operatore di lughezza # per le tabelle array con indici non lineari? Per esempio, qual è il risultato del seguente codice:
 local t = {}
 t[1] = 1
 t[2] = 2
 t[1000] = 3

 print(#t)

Mentre con questo cosa verrà stampato?
 local t = {}
 t[1000] = 123

 print(#t)

Avrete certamente capito che l'operatore # tiene conto solamente degli elementi con indici a cominciare da 1 che proseguono senza 'buchi' di 1 in 1. Infatti, l'operatore di lunghezza # considera il valore nil come termine della tabella, ma è usato molto spesso per inserire progressivamene elementi in una tabella:
local t = {}
for i = 1,100 do
    t[#t+1] = i*i
end

print(t[100])

Secondo problema: il ciclo while

Passiamo a scrivere il codice per inserire in una tabella i fattori primi di un numero. Fatelo per esercizio e poi confrontate il codice seguente che utilizza l'operatore modulo % (resto della divisione intera):
local factors = {}
local n = 123456789

local div = 2
while n > 1 do
    if n % div == 0 then
        factors[#factors + 1] = div
        n = n / div
        while n % div == 0 do
            n = n / div
        end
    end
    div = div + 1
end

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

Così abbiamo introdotto anche il ciclo 'while' perfettamente coerente con la sintassi dei costrutti visti fino a ora: il blocco ripetuto fino a che la condizione è vera è obbligatoriamente definito da due parole chiave, quella di inizio è 'do' e quella di fine è 'end'.

Intermezzo

Siamo all'intervallo!
Non ci sarà pubblicità ma ulteriori notizie su Lua!
In Lua non è obbligatorio inserire un carattere delimitatore sintattico ma è facoltativo il ';'.
I caratteri spazio, tabulazione e ritorno a capo vengono considerati dalla grammatica come separatori, perciò si è liberi di formare il codice con identazione o anche più istruzioni sulla stessa linea.
Solitamente non si utilizzano i punti e virgola finali, ma se ci sono due assegnazioni sulla stessa linea --- stile sconsigliabile perché brutto --- li si può separare con un ';'.
Come sempre una forma stilistica chiara e semplice vi aiuterà a scrivere codice più leggibile e semplice da comprendere a distanza di tempo.

Generalmente è buona norma definire le nuove variabili più vicino possibile al punto in cui verranno utilizzate per la prima volta, un beneficio per la comprensione ma anche per la correttezza del codice perché forse eviterà di fare confusione con i nomi e magari introdurre errori.
Ok. Intervallo finito...

Terzo problema: il ciclo for con step

Altro tema: verificare se un numero è palindromo ovvero che gode della proprietà che le cifre decimali sono simmetriche, come per esempio avviene per il numero 123321. Prima provate a scrivere il codice Lua per conto vostro e poi confrontate questa soluzione:
local digit = {}
local n = 123321

local num = n
while num > 0 do
    digit[#digit + 1 ] = num % 10
    num = (num - num % 10) / 10
end

local sym_n, dec = 0, 1
for i=#digit,1,-1 do
    sym_n = sym_n + digit[i]*dec
    dec = dec * 10
end

print(sym_n == n)

La soluzione utilizza una tabella per memorizzare le cifre del numero che vengono poi utilizzate nel ciclo for a partire dall'ultima (la cifra più significativa) fino alla prima per ricalcolare il numero con le cifre invertite. Per esempio se il numero fosse 123 l'algoritmo restituirebbe 321. Se il numero iniziale è palindromo allora il corrispondente numero a cifre invertite è uguale al numero di partenza.

Nel ciclo for il terzo parametro opzionale -1 imposta il passo per la variabile i che quindi passa dal numero di cifre del numero da controllare (6 nel nostro caso) ad 1.

In effetti non è necessaria la tabella:
local n = 123321

local num, sym_n, dec = n, 0, 1
while num > 0 do
    sym_n = sym_n + (num % 10)*dec
    dec = 10 * dec
    num = (num - num % 10) / 10
end

print(sym_n == n)

Quarto problema: if a rami multipli

Il prossimo problema è il seguente: determinare il numero di cifre di un intero. Confrontate ancora una volta il codice proposto solo dopo aver cercato una vostra soluzione.
local n = 786478654
local digits
if n < 10 then
    digits = 1 -- attenzione non 'local digits = 1'
elseif n < 100 then
    digits = 2
elseif n < 1000 then
    digits = 3
elseif n < 10000 then
    digits = 4
elseif n < 100000 then
    digits = 5
elseif n < 1000000 then
    digits = 6
elseif n < 10000000 then
    digits = 7
elseif n < 100000000 then
    digits = 8
elseif n < 1000000000 then
    digits = 9
elseif n < 10000000000 then
    digits = 10
else -- fermiamoci qui...
    digits = 11
end

print(digits)

Questo esempio mostra in azione l'if a più rami che in Lua svolge la funzione del costrutto 'switch' presente in altri linguaggi, che introduce una nuova parola chiave: 'elseif'.
L'esempio è interessante anche per come viene introdotta la variabile 'digits', cioè senza inizializzarla per poi assegnarla nel ramo opportuno dell'if.
Infatti una variabile interna a un blocco non sopravvive oltre esso, quindi dichiararla all'interno dell'if non è sufficiente.

Attenzione a non premettere 'local' nelle assegnazioni dei rami del condizionale: in questo caso verrebbe creata una nuova variabile locale al blocco con lo stesso nome della variabile esterna che viene oscurata. In altri termini, al termine del blocco 'digits' varrebbe ancora nil perché avremmo modificato una variabile locale al blocco.

Esercizi

Contare quanti interi sono divisibili sia per 2 che per 3 nell'intervallo [1, 10000]. Suggerimento:utilizzare l'operatore modulo '%' resto della divisione intera tra due operandi.

Determinare i fattori del numero intero 5461683.

Instanziare la tabella seguente di tre tabelle/array di tre numeri e calcolarne il determinante.
 t = {
    { 0,  5, -1},
    { 2, -2,  0},
    {-1,  0,  1},
}

Data la tabella seguente stampare in console il conteggio dei numeri pari e dei numeri dispari contenuti in essa. Verificare con un costrutto 'assert()' che la somma di questi due conteggi sia uguale alla dimensione della tabella.
 t = {
    45, 23, 56, 88, 96, 11,
    80, 32, 22, 85, 50, 10,
    32, 75, 10, 66, 55, 30,
    10, 13, 23, 91, 54, 19,
    50, 17, 91, 44, 92, 66,
    71, 25, 19, 80, 17, 21,
    81, 60, 39, 15, 18, 28,
    23, 10, 18, 30, 50, 11,
    50, 88, 28, 66, 13, 54,
    91, 25, 23, 17, 88, 90,
    85, 99, 22, 91, 40, 80,
    56, 62, 81, 71, 33, 30,
    90, 22, 80, 58, 42, 10,
}

Data la tabella precedente scrivere il codice per costruire una seconda tabella uguale alla prima ma priva di duplicati senza alterare l'ordine delle cifre.

Data la tabella precedente costruire una tabella le cui chiavi siano i numeri contenuti in essa e i valori siano il corrispondente numero di volte che la chiave stessa compare nella tabella di partenza. Stampare poi in console il numero che si presenta il maggior numero di volte.

Esempio spaziale

Data la tabella Lua riportata nel seguito con dati sugli orbiter del programma Space Shuttle della Nasa, scrivere un programma per calcolare:
  • il numero totale dei voli effettuati dagli orbiter;
  • il numero totale degli attracchi alla Stazione Spaziale Internazionale (ISS);
  • il tempo medio di costruzione in anni degli orbiter (è consentito assumere i mesi pari a 30 giorni);
  • per ciascun orbiter, il tempo in giorni trascorso tra la prima missione e l'ultima;
  • il nome dell'orbiter che a volato per primo.

Un quiz articolato (mentre scrivevo le domande mi sembrava di essere a Rischiatutto con Mike Buongiorno).

-- Space Shuttle Missions Database. Version 1.8.
-- from: https://sites.google.com/site/monzitrek/missioni-shuttle
-- by: © 2005-2011 - Ing. Luigi Morielli
-- licence: http://creativecommons.org/licenses/by-nc-sa/2.5/it/

-- per info consultare anche i siti dell'associazione ISAA:
-- http://www.isaa.it/
-- http://www.forumastronautico.it/
-- http://www.astronauticast.com/

Shuttle_DB = {-- tables' array
    {
        orbiter_id = "OV-099", orbiter_name ="Challenger",
        flight = 10,
        ISS_docking =  0,
        start_costruction = {d=24, m=06, y=1975},
        end_costruction = {d=23, m=10, y=1981},
        first_mission = {d=04, m=04, y=1983},
        last_mission = {d=28, m=01, y=1986},
        cause = "destroyed",
    },
    {
        orbiter_id = "OV-102", orbiter_name ="Columbia",
        flight = 28,
        ISS_docking =  0,
        start_costruction = {d=27, m=03, y=1975},
        end_costruction = {d=08, m=03, y=1979},
        first_mission = {d=12, m=04, y=1981},
        last_mission = {d=01, m=02, y=2003},
        cause = "destroyed",
    },
    {
        orbiter_id = "OV-103", orbiter_name ="Discovery",
        flight = 39,
        ISS_docking = 13,
        start_costruction = {d=27, m=08, y=1979},
        end_costruction = {d=25, m=02, y=1983},
        first_mission = {d=30, m=08, y=1984},
        last_mission = {d=24, m=02, y=2011},
        cause = "retired",
    },
    {
        orbiter_id = "OV-104", orbiter_name ="Atlantis",
        flight = 33,
        ISS_docking = 12,
        start_costruction = {d=03, m=03, y=1980},
        end_costruction = {d=10, m=04, y=1984},
        first_mission = {d=03, m=10, y=1985},
        last_mission = {d=08, m=07, y=2011},
        cause = "retired",
    },
    {
        orbiter_id = "OV-105", orbiter_name ="Endeavour",
        flight = 25,
        ISS_docking = 12,
        start_costruction = {d=15, m=02, y=1982},
        end_costruction = {d=06, m=07, y=1990},
        first_mission = {d=07, m=05, y=1992},
        last_mission = {d=16, m=05, y=2011},
        cause = "retired",
    },
}

Riassunto della puntata

Finalmente un po' di codice in Lua! Abbiamo incontrato le particolarità dell'operatore lunghezza '#' e i costrutti di base come i cicli 'for', 'while' e il condizionale 'if'.

Proseguiamo nella prossima puntata a presentare nuovi elementi del linguaggio attraverso esempi di codice. Sarà la volta degli operatori logici 'and', 'not' e 'or' e delle operazioni sulle stringhe.

mercoledì 25 marzo 2015

Da jpg a png con l'aiuto di sed. [Brevi Esercizi]

Salve!

Come convertire svariate foto da jpg a png.

Prerequisito

$ sudo apt-get install imagemagick


Le foto il cui formato va convertito:


La riga di comando:

for i in *.jpg; do j=$(echo $i | sed -e 's/\.jpg/\.png/'); convert $i $j; done;


Le foto convertite in png.


Alla prossima!

Un esempio di contabilità con AWK [Brevi Esercizi]

Salve!

Ogni tanto ritorno ad awk per svolgere compiti semplici semplici. Forse avrei dovuto scrivere per risolvere problemi semplici con uno strumento estremamente potente come awk.

Andiamo a noi. Ho abbozzato una tabella (incompleta) contenente somme di denaro in entrata(+) e in uscita(-). Lo scopo è quello di avere somme per colonne, per righe e, alla fine, ottenere il totale (rimanente).

tab

Gen -508 +133 -900 
Feb +981 -240 +325 
Mar +450 +340 +435 
Apr +111 +234 +556
Mag +768 -998 +431
Giu +901 +123 +877
Lug +213 +356 +678
Ago +321 +653 +876

test.awk

#!/usr/bin/awk -f
 
  BEGIN { 

         
         print "\n\t\t_____Contabilità 2014_____\n";
         FS=" "; OFS = "\t";
  }                                                   
    { 
      Mese="//"; a += $2; b += $3; c += $4;
      tot = ($2+$3+$4); 
      print "\t" $1,$2,$3,$4,"|  "tot;
    }
  END { 
       print "\t________________________________\n";
       print "\t" Mese,a, b, c, "--> "(a+b+c)" (Totale)","\n";
 }

Risultato: 



Alla prossima!

domenica 22 marzo 2015

Modern C++ - Classi e Strutture di dati

Ben tornati al corso di Modern C++ qui sulle pagine del blog di Lubit :)

Oggi introduciamo qualcosa di piu' complesso: le classi e le strutture.
Il C++ e' stato uno dei primi linguaggi di programmazione ad oggetti, oggetti intesi come contenitori di dati ed operazioni. In particolare, un oggetto e' una istanza di una classe. Ad esempio, l'oggetto "Station Wagon" e' una istanza della classe "Auto".

Una classe viene definita con la parola chiave class ed e' definita da un costruttore e da un distruttore, oltre alle variabili e alle funzioni interne, definite membri. Le funzioni e le variabili possono esser di tre tipi:
  • pubbliche (public), ovvero visibili dall'esterno
  • protette (protected), ovvero visibili dagli oggetti di classi derivate
  • private (private), ovvero utilizzabili solo dall'oggetto stesso
Il costruttore, come dice la parola stessa, si occupa di generare un oggetto, inizializzandone le eventuali variabili interne e richiamando eventuali funzioni interne. In questo modo, si esegue l'allocazione delle risorse in modo tale poi da procedere con l'esecuzione delle sue funzioni membro (per gli amanti degli approfondimenti, consiglio di leggerne di piu' qui).

Il distruttore esegue l'operazione opposta: si occupa di liberare le risorse allocate.

La dichiarazione di una classe in C++ viene effettuata nel seguente modo:

class NomeClasse
{
  public:
    NomeClasse();
    ~NomeClasse();

    void funzioneClasse();
    bool funzioneClasse2();

  private:
    bool m_flag;
    int* m_puntatore;
};

Il costruttore ha lo stesso nome della classe, mentre il distruttore ha lo stesso nome della classe, con in piu' il prefisso "~".

Al termine delle parentesi graffe, va' inserito il punto e virgola per completare la dichiarazione. La definizione delle funzioni puo' avvenire all'interno della stessa classe oppure in separata sede, come nel seguente esempio:

NomeClasse::NomeClasse()
{
  m_flag = false;
  m_puntatore = new int();
}

NomeClasse::~NomeClasse()
{
  if(m_puntatore != nullptr)
    delete m_puntatore;
}

bool NomeClasse::funzioneClasse2(){}

int NomeClasse::funzioneClasse(){}

In C++11, e' possibile inizializzarele variabili all'interno della classe stessa:

class NomeClasse
{
  public:
    NomeClasse();
    ~NomeClasse();

    void funzioneClasse();
    bool funzioneClasse2();

  private:
    bool m_flag{false};
    int* m_puntatore{nullptr};  //lo inizializzo con nullptr
};


In questo modo, non occorre inizializzare nel costruttore queste variabili, evitando di dimenticarsi di farlo.

Come gia' detto, le parole chiavi public, private e protected regolano la visibilita' delle funzioni e delle variabili. E' buona prassi mantenere le variabili private, le quali pero' sono accessibili mediante apposite funzioni public, che permettono di leggerne il valore e di modificarlo. Ad esempio,

class Auto
{
  public:
    Auto();
    ~Auto();

    void setSpeed(int speed);
    int getSpeed();

  private:
    int m_speed{0};
};

void Auto::setSpeed(int speed)
{
  m_speed = speed;
}

int Auto::getSpeed()
{
  return m_speed;
}

In tal modo, la variabile m_speed e' modificabile mediante le due funzioni setSpeed() e getSpeed(). Nell'ottica di grandi progetti, fa comodo impostare le classi nel modo suddetto, onde evitare spiacevoli inconvenienti, specialmente con la programmazione multithread.

Per creare un oggetto di tipo Auto, basta scrivere:

Auto mia_auto;    //nello Stack

Auto *auto_puntatore = new Auto();       //nell'Heap, C-Style

std::unique_ptr auto_smart(new Auto());  //nell'Heap, C++11 Style

mia_auto.setSpeed(200);

auto_puntatore->setSpeed(200);

auto_smart->setSpeed(200);

L'accesso ai membri di una classe vengono regolati mediante l'operatore "." oppure "->", nei casi in cui si tratti di variabile semplice oppure di un puntatore.

Le strutture, definite con la parola chiave struct, sono praticamente equivalenti ad una classe, i cui membri sono tutti pubblici, ovvero le variabili e le funzioni sono visibili dall'esterno:


struct Auto
{
  int m_speed{0};
  int m_doors{5};
  bool m_run{false};
};

L'accesso alle variabili viene regolato allo stesso modo di una classe. Per questa lezione e' tutto, nella prossima puntata vedremo l'overloading delle funzioni e i costruttori di copie! Alla prossima :)