martedì 21 dicembre 2010

Esercizi /1

Di seguito una prima infornata di esercizi, nei prossimi giorni (anche su vostra specifica richiesta) ne inserirò altri. È possibile discutere degli esercizi direttamente nelle risposte a questo post: io risponderò alle vostre domande in modo da costruire una base di conoscenza con i dubbi più comuni. Vi prego solo di non postare le RISPOSTE né degli indizi troppo palesi: lasciate che tutti possano spaccarsi la testa per trovare la risposta per conto proprio. Questo è il senso degli esercizi. Se volete una conferma della vostra soluzione scrivetemi in privato.


Esercizietti:

1) Convertire in esadecimale il numero decimale 1814512 (Consiglio: può essere più semplice passare attraverso la codifica binaria)
2) Quanti bit sono necessari per descrivere il numero esadecimale FECC1A
3) Convertire in decimale il numero binario 110101101
4) Convertire in binario il numero decimale 531
5) Il programma Calc può rappresentare fino alla colonna AMJ: quante sono?
6) Un segnale audio della durata di 10 secondi deve essere campionato. Se la frequenza di campionamento è 22000 campioni al secondo e ogni campione ha un’espressività numerica di 12 bit, quanti byte saranno necessari per rappresentare il segnale così campionato?
7) Si desidera fare la scansione di una fotografia 10x5 cm. Se in ogni cm^2 vengono rilevati 1024 pixel, ognuno dei quali ha un colore scelto fra 65536 possibili, quanti byte saranno necessari per conservare le informazioni dell’immagine in formato non compresso?
8) Usare l’algoritmo bubble-sort per ordinare le seguenti lettere “akrdstopqaw”. Scrivere una copia del vettore dopo ogni passaggio di ordinamento.
9) Si hanno 5 carte coperte da ordinare, con stampigliato sul retro le lettere A B C D E. Il valore delle carte non è noto, ma si sa che D<A; A<C; B<A; D<C; C<E. Applicando il bubble-sort ordinare il mazzo di carte sulla base di queste sole informazioni (spiegando il procedimento)
10) Data la memoria [a=1][t=0][v=7,3,5,4,9,2,1,6] eseguire il seguente programma e scrivere il contenuto della memoria alla fine. Qual è lo scopo del programma?
RIPETI FINCHÉ (a <= 8)
{t = t + v[a]
a = a + 2
}
11) Un papà premuroso è con la famiglia in spiaggia, al bagno numero 1. Il bimbo chiede un gelato ma non sa dire quale gusto preferisce. Supponendo che in ogni bagno ci sia un gelataio che vende un unico gusto di gelato e che questo gusto sia diverso da tutti gli altri, il papà dovrà andare e tornare da ogni bagno per far assaggiare il gelato al bambino. Qual è la complessità computazionale dell’algoritmo del papà? Se il gelato viene trovato al bagno numero 10, quanti bagni avrà dovuto attraversare?
12) Un contabile truffaldino deve coprire un ammanco di bilancio con delle fatture false di cui è già in possesso. Per camuffare il suo operato deve coprire esattamente la cifra mancante scegliendo alcune di queste fatture il cui totale deve coincidere precisamente con l’ammanco. Si è in grado di consigliargli un sistema efficiente per mettersi al sicuro? (che non sia emigrare in Patagonia...)
13) Codificando le lettere dell’alfabeto con 6 bit, crittare il messaggio “universita del salento” con la chiave “informatica” usando l’operatore XOR.
14) Nei moderni sistemi operativi è possibile far funzionare contemporaneamente più programmi. Questa affermazione è corretta?
15) Scrivere una piccola struttura in XML per mantenere le informazioni di amici. Le informazioni necessarie sono Nome, Cognome, Indirizzo di casa e Indirizzo dell’ufficio (a loro volta composti da Via, Civico, CAP e Città), la Email, e un numero imprecisato di numeri di telefono senza particolare ordine, ma con la posibilità di indicarne uno preferito.
16) Decomprimere il testo compresso: “Sopra la panca[9,4]ca[3,4]cam[10,3] sotto[9,4][10,5][9,4][20,7] crepa.” “Sopra la panca[6,4]ca[3,4]cam[10,2] sotto[6,4][10,5][6,4][20,7] crepa.” mostrando ogni passaggio.
17) Inserendo l’espressione regolare “<b>([^<]*)(<center>[^<]*</center>)?([^<]*)</b>” in Cerca e inserendo “$1$2$3” in sostituisci, come si desidera modificare il sorgente in HTML di una generica pagina web? (notate che l’espressione [^<] significa “qualsiasi simbolo tranne <”).
18) Mettendo di avere il foglio di Calc come in figura, supponendo che esista una funzione SOMMA.DISPARI(celle) che restituisce il totale ottenuto sommando solo I valori dispari, quale formula devo scrivere nella cella evidenziata per avere il totale dei soli numeri pari?

Lezione del 21 dicembre 2010 /2 (Ultima lezione)

La lezione ha presentato alcuni esercizi e alcuni punti da tenere ben presente durante l'esame:
  • se la soluzione che state pensando appare troppo complicata e macchinosa allora probabilmente state seguendo una strada sbagliata: gli esercizi possono apparire intricati e mostruosi, ma al 99% la soluzione è molto semplice (ma non è semplice il percorso per raggiungerla)
  • Tranne alcune eccezioni (xor, bit, cambi di base) il livello al quale dovete rimanere è quello logico-concettuale: non infilatevi in tecnicismi e complicatezze che non sono rilevanti per l'esercizio.
  • Vi potete inventare le cose: potete aggiungere presupposti (purché siano logici, chiari e coerenti) e inventare funzioni che fanno calcoli semplici funzionali alla vostra soluzione. (Per esempio in un programma potrebbe essere necessario sapere se un valore è pari o dispari: una informazione molto semplice eppure straordinariamente complessa per chi non è addentro ai tecnicismi. Potete perciò assumere che esista una funzione PARI(n) che dice se un numero è pari. Questo però non potete farlo ovviamente se la funzione che postulate è la soluzione dell'esercizio!) Siate creativi.
  • Non vi impiccate alle definizioni, cercate di rispondere alle domande secondo una vostra comprensione dell'argomento, crcando di verificare la coerenza logica di quello che sostenete. È preferibile una risposta meno rigidamente esatta ma più creativa piuttosto che la ripetizione delle definizioni lette da qualche parte sul libro o anche dette a lezione. Questo però non deve essere un alibi per affrontare l'esame con una preparazione superficiale.
  • Lo scopo dell'esame è verificare la vostra comprensione della materia, quindi quello che dovete fare è convincermi di questo fatto. Solo questo dev'essere il criterio che orienterà le vostre risposte. È molto meglio una risposta parziale ma convincente che una completa ma asettica: la competenza si vede soprattutto nella capacità di decide cosa è rilevante e cosa non lo è. Sfoggiare quanto uno è stato bravino a studiare non è una buona strategia per avere un buon voto.
Al 90% l'esame sarà così composto: un preaccertamento fatto in laboratorio, in caso di superamento si passa alla prova scritta. Lo scritto prevederà sia definizioni che esercizi, e avrà un'impostazione fondamentalmente logico/teorica e poco nozionistica. L'orale non è né facoltativo né obbligatorio: lo posso richiedere io per verificare alcuni aspetti dello scritto. Potete propormi di sostenere un esame orale, ma siate consapevoli che l'esito dell'orale può essere uno qualunque di quelli possibili e quindi non necessariamente di miglioramento del risultato dello scritto.

Lezione del 21 dicembre 2010 /1


Lezione del 21 dicembre 2010 /1 (Parte 1 di 3) PSINFORM from Marco Tonti on Vimeo.


Lezione del 21 dicembre 2010 /1 (parte 2 di 3) PSINFORM from Marco Tonti on Vimeo.



Leione del 21 dicembre 2010 /1 (parte 3 di 3) PSINFORM from Marco Tonti on Vimeo.

La mail spedita a lezione che era finita nello spam:




Il contenuto "reale" della email, come viene trasmessa effettivamente con tutte le informazioni supplementari aggiunte dai vari server che si sono occupati dell'invio e della ricezione (ho sostituito le @ con §):

Delivered-To: marco§tonti.info
Received: by 10.231.17.199 with SMTP id t7cs22633iba;
Tue, 21 Dec 2010 01:50:25 -0800 (PST)
Received: by 10.216.7.205 with SMTP id 55mr8440841wep.96.1292925024001;
Tue, 21 Dec 2010 01:50:24 -0800 (PST)
Return-Path:
Received: from ateneo.unile.it (ateneo.unile.it [193.204.68.3])
by mx.google.com with ESMTPS id t11si6016381wes.103.2010.12.21.01.50.23
(version=TLSv1/SSLv3 cipher=RC4-MD5);
Tue, 21 Dec 2010 01:50:23 -0800 (PST)
 Received-SPF: neutral (google.com: 193.204.68.3 is neither permitted nor denied by best guess record for domain of marco.tonti§unisalento.it) client-ip=193.204.68.3;
 Authentication-Results: mx.google.com; spf=neutral (google.com: 193.204.68.3 is neither permitted nor denied by best guess record for domain of marco.tonti§unisalento.it) smtp.mail=marco.tonti§unisalento.it
Received: from ateneo.unile.it (unknown [10.0.211.142])
by ateneo.unile.it (Postfix) with SMTP id 99AB014D8607
for ; Tue, 21 Dec 2010 10:47:14 +0100 (CET)
 Message-Id: <20101221094732.99AB014D8607§ateneo.unile.it>
Date: Tue, 21 Dec 2010 10:47:14 +0100 (CET)
 From: marco.tonti§unisalento.it
To: undisclosed-recipients:;
X-ateneo.unile.it-MailScanner-Information: Please contact the ISP for more information
X-ateneo.unile.it-MailScanner: Found to be clean
X-ateneo.unile.it-MailScanner-SpamScore: s
 X-ateneo.unile.it-MailScanner-From: marco.tonti§unisalento.it
X-Spam-Status: No

ciao marco come va?

Lezione del 20 dicembre 2010 /2

Fondamenti di HTML

Lezione del 20 dicembre 2010 PSINFORM from Marco Tonti on Vimeo.

lunedì 20 dicembre 2010

Lezione del 20 dicembre 2010 /1

C'è stato un addedum alla lezione sui sistemi operativi:
Come abbiamo visto i programmi possono essere scomposti in sottoprocedure più semplici. Questa scomposizione rende il programma più facile da leggere e da correggere. Ma c'è anche un ulteriore vantaggio: molto spesso queste sottoprocedure (e funzioni) hanno un ruolo e un funzionamento molto generale (un esempio l'abbiamo visto: la procedura "scambia" può essere applicata a qualsiasi vettore quindi la si può "riciclare" per tutti i programmi che facciamo). Molto spesso i programmatori raccolgono le loro funzioni sviluppate nel corso del tempo in delle cosiddette Librerie di funzioni (che in Windows sono file a sé stanti con estensione .dll = Dynamic Link Library), oppure usano librerie costruite da altri o anche appartenenti al sistema operativo. Queste librerie vengono raggruppate tematicamente, per esempio una libreria che effettua calcoli matematici avanzati, un'altra con istruzioni per la grafica, un'altra per la crittografia e così via.
Dato che un programma per funzionare necessita di tutte le librerie, quando si produce un programma che debba essere utilizzato su altri sistemi diversi dal proprio è necessario impacchettare tutto insieme e trasportare anche le librerie (che stanno in file separati) insieme al file eseguibile, quello che contiene il programma vero e proprio. Inoltre quando si introduce un programma in un sistema è necessario aggiornare alcune informazioni speciali che il sistema operativo mantiene, come l'icona da usare per i file di un certo programma, l'applicazione da lanciare quando si fa doppio-clic sui file di dati (come succede per i documenti di Word o di Writer). Le librerie inoltre vanno inserite in cartelle speciali del sistema operativo. Tutte queste operazioni sono effettuate durante il processo (automatico) di installazione. Questa operazione è vera a maggior ragione per i driver, essendo questi dei piccoli programmi che vanno a far parte integrante del sistema operativo. Questa è la ragione per cui non è possibile solo copiare una cartella di un programma per trasportalo da un computer a un altro: è necessario il pacchetto intero che contiene tutti file necessari.

Il resto della lezione ha trattato rapidamente i concetti legati ai sistemi di DataBase. L'esempio più classico e accessibile (molto spesso i motori DB sono programmi sofisticati da mettere su un server, o librerie usate da altri programmi) è Access. I database utilizzabili sul proprio computer sono denominati DataBase Desktop (un altro esempio è Filemaker della Apple, prodotto anche per Windows).
Durante la lezione si è discusso di Entità e di Relazioni (e di Chiavi) con l'esempio di come potrebbe essere il sistema di gestione degli studenti dell'Università, con tabelle che registrano gli esami sostenuti, il voto relativo, la presenza di lode, il piano di studi, il corso di laurea. Alla fine della lezione sono stati presentati anch eun paio di esempi di interrogazioni (query in gergo) per calcolare la media di uno studente o contare il numero di lodi. Le query vengono scritte in un linguaggio standard che si chiama SQL (Structured Query Language).

L'argomento è trattato molto approfonditamente nel libro di testo ai capitoli 12, 13 e 14. La lettura dei capitoli è richiesta, specialmente in quelle parti dove si affrontano specificamente gli argomenti trattati. È possibile non soffermarsi sui linguaggi di interrogazione e sui dettagli tecnici più approfonditi, ma è fondamentale essere certi di aver colto l'insieme dei concetti di fondo.


giovedì 16 dicembre 2010

Calendario lezioni settimana del 20 dicembre

Si terranno di mattina due lezioni straordinarie, oltre quelle ordinarie:
lunedì 20 dalle 9.30 alle 11 (straordinaria)
lunedì 20 dalle 17 alle 18.30
martedì 21 dalle 9.30 alle 11 (straordinaria)
martedì 21 dalle 17 alle 18.30

Lezione del 16 dicembre 2010


Lezione del 16 dicembre 2010 PSINFORM from Marco Tonti on Vimeo.

Argomenti:
  • completamento della parte sui fogli di calcolo
  • grafici
  • incolla speciale
  • tecnologia OLE (object linking and embedding)
  • programma Draw di OpenOffice per il disegno di grafici e schemi 

mercoledì 15 dicembre 2010

Lezione del 15 dicembre 2010

Introduzione ai fogli di calcolo

L'argomento richiede l'estensione di argomenti già affrontati in precedenza. In particolare il concetto di sottoprocedura viene espeso in senso funzionale, cioè una sottoprocedura è una serie di operazioni che restituisce un valore. Un primo semplice esempio:

SOMMA(a, b)
{ return a+b }

è una sottoprocedura che restituisce ("return" è il termine che tradizionalmente si usa) la somma di a e di b. Quindi se troviamo in un programma x=SOMMA(3,7), x assumerà il valore 10. Si chiamano funzioni proprio perché è identico al concetto di funzione matematica.
Un esempio più verosimile (il + non è erto necessario metterlo in una funzione!) è la somma degli elementi di un vettore, che possono essere per esempio i movimenti di un conto bancario dei quali si può voler calcolare il totale. v è il generico vettore che contiene i valori da sommare (può essere anche quello delle carte da gioco ovviamente!), mentre n è il numero degli elementi da sommare:

SOMMA(v, n) 
{
i=1
s=0 
RIPETI FINCHE'(i<=n)
   {
   s = s+v[i]
   i = i+1
   }
return s


Come abbiamo già visto, dato che la memoria è unica all'interno di un programma, è concettualmente possibile riferirci direttamente alle locazioni di memoria, quindi potremmo definire una funzione del tutto simile alla precedente ma che invece di sommare gli elementi di un vettore somma i valori della memoria, da una certa posizione a un'altra posizione. La procedura, la cui modifica è piccolissima (con l'introduzine convenzionale del vettore MEMORIA che si riferisce all'intera memoria dle programma), diventa:

SOMMA(inizio, fine) 
{
i=inizio
s=0 
RIPETI FINCHE'(i<=fine)
   {
   s = s+MEMORIA[i]
   i = i+1
   }
return s


Come già discusso la memoria nella quale opera un programma si può considerare coma una striscia seminfinita di celle che contengono valori. Per comodità è possibile immaginare come se la memoria andasse "a capo" a un certo punto, diciamo per esempio dopo 100 celle (proprio come le pagine di Write) il nastro della memoria può essere spezzato e fatto continuare in basso. La numerazione rimane identica, quindi ogni cella può essere riferita sempre in base al suo ordine.

Ma dato che abbiamo trasformato il nastro unidimensionale in una "pagina" di locazioni bidimensionali, possiamo pensare di riferirci alle celle con le coordinate (come una matrice), la riga e la colonna. Ogni cella è identificata univocamente dalle sue coordinate, e ogni coppia di coordinate "lecite" identifica esattamente una cella.

Questa memoria, così organizzata, è esattamente quello che ci appare quando apriamo un programma di foglio di calcolo come "Calc" della suite OpenOffice sulla quale lavoriamo. Quello che ci appare è una prateria incontaminata di celle di memoria, dentro le quali possiamo inserire tutto quello che ci pare. Nei programmi di questo tipo normalmente le colonne vengono indicate dalle lettere dell'alfabeto, o da loro combinazioni (esercizietti: come descrivere le colonne quando superano la Z? Qual è l'ultima colonna di Calc? QUANTE sono in totale? -- IN DECIMALE!)


Lezione del 15 dicembre 2010 PSINFORM from Marco Tonti on Vimeo.

martedì 14 dicembre 2010

domenica 12 dicembre 2010

mercoledì 8 dicembre 2010

Lezione del 7 dicembre 2010


Lezione del 7 dicembre 2010 PSINFORM from Marco Tonti on Vimeo.

Espressioni regolari (note anche, e più brevemente, come RegEx da regular expressions. QUI per chi comprende l'inglese un sito eccellente ma non guardate in "about the site" per non prendere paura!, e QUI un sito dove fare prove)
Si tratta di un sistema quasi standard (con poche variazioni tra le varie implementazioni) per decrivere in modo più versatile le stringhe che si desidera cercare usando le comuni funzioni "cerca" di molti programmi di editor di testo, in particolare quello adottato dal nostro corso, Writer di OpenOffice. Per eseguire la ricerca nel modo standard è sufficiente aprire la finestra di ricerca e inserire la parola da cercare. Il "match", cioè la corrispondenza tra la paola cercata (in realtà una sequenza di caratteri) e i caratteri che compongono il testo, può essere vincolato in due modi: indicando che la parola trovata sia una parola intera (quindi "arco" non verrà trovato in "Marco" ma verrà trovato in "prese l'arco e centrò la mela": la punteggiatura è considerata un'interruzione di parola), o che la corrispondenza debba essere anche tra maiuscole e minuscole (quindi "Marco" verrebbe trovato in "Marco Tonti", ma non in "una moneta da un marco").

Le regex permettono di introdurre una varietà infinitamente più sofisticata nella modalità di descrizione di una stringa. Le RegEx (attivate in Writer nelle opzioni avanzate della ricerca) permettono per esempio di esprimere una stringa da cercare il cui matching può o meno comprendere un carattere, per esempio cercando "Marco?" (dove il "?" è un simbolo del linguaggio delle regex che indica che l'ultimo carattere può o meno essere presente) il programma troverebbe corrispondenze sia con "Marco Tonti" che con "Marc Chagall", perché abbiamo espresso nella ricerca l'informazione che la "o" finale può essere opzionale.
Un'altra possibilità che sarebbe utile è quella di poter indicare delle alternative che può avere un carattere in una certa posizione, per esempio se volessimo cercare (contemporaneamente) tutti i "Marco" e tutti i "Mario", potrebbe essere utile un modo per indicare che il quarto carattere possa essere sia una "c" che una "i". Nelle espressioni regolari per indicare una serie di scelte accetabili per un carattere si usano le parentesi quadre, all'interno delle quali inserire i caratteri accettabili in quella posizione, quindi se noi cercassimo "Mar[ic]o" esprimiamo il fatto che il quarto carattere può essere sia una "i" che una "c". Nelle parentesi quadre è possibile inserire anche cifre e intervalli, per esempio [a-z] indica tutti i caratteri dell'alfabeto (senza accentate, cifre ecc. solo le lettere). È possibile fare in modo che le quadre funzionino anche al contrario, cioè per indicare quali lettere NON devono essere presenti, questo lo si ottiene scrivendo [^ ] (oppure, in certe implementazioni, [! ]). Cercando "Mar[^c]io", potremmo trovare tutte le sequenze "Maraio", "Marbio", "mardio", "mareio"; notate però che "marcio" non è presente, perché imponiamo che in quella posizione NON possa esserci una "c" (e inoltre non siamo in Danimarca).
Esistono inoltre caratteri speciali che si usano come quantificatori. Uno l'abbiamo già introdotto ed è il "?", che significa che il carattere immediatamente precedente può comparire 0 o 1 volte. Per indicare che il carattere precedente può comparire da 0 a un numero indefinito di volte si usa il quantificatore *. Quindi se cercassimo "ab*c" il programma riconoscerebbe qualsiasi sequenza di composta di una "a" seguita da un qualsiasi numero di "b" e conclusa con una "c": "abc", "abbc", "abbbc", "abbbbc"... ma anche "ac" dove la "b" centrale compare 0 volte! Se vogliamo stringere il vincolo possiamo usare il quantificatore + che è molto simile al * ma impone che la sequenza sia lunga almeno un carattere. Scrivendo "ab+c" riconosceremmo "abc", "abbc", "abbbc" e così via ma non "ac". Lo stesso risultato lo potremmo ottenere con "abb*c", che si può leggere come: "una a seguita da una b seguita da una qualsiasi sequenza di b seguita da una c". La notazione col + però è più compatta e facile da leggere.
Provando a combinare gli elementi introdotti finora, cercando con la regex "[abc]+" otterremmo il riconoscimento di tutte le possibili sequenze di combinazioni dei caratteri a, b, c. Potremmo trovare "a", "b", "c", "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc", "aaa" e così via: qualsiasi combinazione di qualsiasi lunghezza dei caratteri a b c verrebbe riconosciuta, e SOLO quelle combinazioni verebbero riconosciute. "abcbaabababababacbcabcabcabcabcabacbcabccacbacbacacacbacbacbbcbcbcbbabcbcabbcbcbcbcabcbacb" sì, "aaaaaaaaa" sì, "bbbbbb" sì, "acab" sì, "bacca" sì, "ada" NO.
Un altro simbolo speciale è il carattere ".", il punto, che fa match con qualsiasi carattere (tranne gli a-capo). "a..b" troverebbe tutte le sequenza di 4 caratteri ai che cominciano con "a" e finiscono con "b". Toverebbe "acab", troverebbe "aaab", troverebbe "abbb". Anche questo carattere speciale può essere combinato con i quantificatori, quindi ".*" è una qualsiasi sequenza di caratteri.
Un altro carattere speciale è il "pipe": "|". Indica un'alternativa tra due espressioni regolari. Per esempio cercando "Marco|Mario" troveremmo tutte le occorrenze di Marco o di Mario. Notate che il | non agisce solo sui caratteri adiacenti, ma si espande a tutte le espressioni regolari verso sinistra e verso destra, quindi è necessario un modo per "contenere" la sua espansione. Per fare ciò in genere le scelte alternative si inseriscono tra parentesi tonde: (|). Le parentesi però hanno un ruolo tutt'altro che marginale nelle Regex, perché possono racchiudere qualsiasi tipo di espressione regolare e vengono considerate com un tutt'uno. Al quale si può applicare anche qualsiasi quantificatore. Per esempio (ab|cd)+ riconosce qualsiasi sequenza di caratteri composta di combinazioni qualsiasi delle sottosequenze "ab" o "cd". Riconoscerebbe "abcd", "cdab", "abababcdcdcd", "abcdabcdabcd". Ma NON "abcda", perché la sequenza si deve ottenere come iterazione di due possibili scelte "fisse": "ab" o "cd". Il quantificatore fa ripetere questa scelta per un numero indefinito di volte.
Riassumendo, i caratteri speciali che si usano nelle espressioni regolari sono:
[ ] un insieme di caratteri ammissibili per una posizione
[^ ] un insieme di caratteri NON ammissibili per una posizione
( ) una sotto-espressione regolare, gruppo
| un'alternativa tra due espressioni regolari
. un qualsiasi carattere
? quantificatore: 0 o 1 occorrenza del carattere (o gruppo) immediatamente precedente
* quantificatore: 0 o n occorrenze del carattere (o gruppo) immediatamente precedente
+ quantificatore: 1 o n occorrenze del carattere (o gruppo) immediatamente precedente
(ulteriori informazioni e approfondimenti si possono trovare ovunque cercando "espressioni regolari" con google, in particolare consiglierei: qui )

L'esempio che abbiamo affrontato a lezione è quello delle date. Le date (in linea di massima) hanno una struttura stabile e prevedibile, in particolare sono sequenze di due cifre, una barra, due cifre, una barra e quattro cifre. Se noi volessimo trovare tutte le date presenti in un documento (cioè tutte le sequenze di caratteri che rispondono a questo pattern) potremmo comodamente usare le espressioni regolari. Un primo approccio può essere il seguente (mettiamo di limitarci alle date del secolo scorso):
[0-9][0-9]/[0-9][0-9]/19[0-9][0-9]
però questa regex riconoscerebbe TUTTE le combinazioni fatte così, per cui anche eventuali sequenze di carattere (come dei codici) che date non sarebbero (riconoscerebbe 74/15/1999). Possiamo fare di meglio, restringendo per via sintattica la variabilità delle date. Intanto consideriamo che i numeri dei giorni possono andare da 01 a 31, quelli dei mesi da 01 a 12. Adeguiamo le descrizioni a questi nuovi vincoli:
[0-3][0-9]/[01][0-9]/19[0-9][0-9]
Anche in questo caso esistono sequenze che possono essere trovate ma che non sono date, per esmepio 39/19/1999. Si può fare di meglio? Certo! Bisogna considerare che certe combinazioni di valori, in particolare i valori dei giorni che cominciano con 3 e dei mesi che cominciano con 1, non sono tutte accettabili e possono essere escluse a priori. Dato che dobbiamo distinguere due casi sia per i giorni che per i mesi, bisogna usare un operatore di | che ci faccia stabilire in quale dei due casi ci troviamo, ricordandosi di raggrupparli in parentesi.
 (0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/19[0-9][0-9]
Analizziamo la sottoespressione dei giorni: 0[1-9]|[12][0-9]|3[01]
 Questa espressione ci pone davanti a tre alternative per il riconoscimento: o la stringa che stiamo cercando comincia con uno 0 (e allora può essere seguta solo da un numero tra 1 e 9: il giorno 00 non esiste), oppure la stringa comincia con un 1 o con un 2 (e allora può essere seguita da qualsiasi cifra), o comincia con un 3 (e allora può essere seguita solo da uno 0 o da un 1). Questa espressione riconosce solo ed esattamente le sequenze di due caratteri che rappresentano numeri compresi tra 01 e 31.
L'espressione dei mesi è analoga:  0[1-9]|1[012]
Se la sequenza comincia con uno 0 allora la cifra dopo dev'essere compresa tra 1 e 9, se comincia con 1 allora può essere seguita solo da 0, 1, 2.
Notate che alcune date sfuggono comuque, ma sono vincoli di natura semantica difficilmente traducibili in sintassi: verrebbe riconosciuta come data anche il 31/02/1999 o il 31/08/1999 (ma febbraio ha al massimo 29 giorni e agosto ne ha 30, quindi queste non sono veramente date). Dopo aver fatto la lezione e dopo aver scritto questo riassunto, guardando sul sito che vi ho segnalato ho trovato questo: http://www.regular-expressions.info/dates.html praticamente le stesse cose che ho descritto qui.
Nei fatti pratici raramente è utile descrivere le date in tanto dettaglio, ma la tecnica è talmente versatile da poter essere usata per esempio per trovare (o verificare) indirizzi email:
[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}
Per comprendere come mai questa espressione è in grado di riconoscere un'email, bisogna aggiungere un dettaglio sull'ultima espressione fra graffe: è un modo per quantificare apertamente una ripetizione, e significa: il carattere precedente ripetuto da 2 a 4 volte (che cattura per esempio i ".it" o ".com" o ".info"). Per collegarlo a quanto detto prima, il quantificatore ? corisponde esattamente a {0,1}. Inoltre l'ultimo "." della regex non è un carattere speciale, perché essendo preceduto da una \ il sistema sa che lo deve trattare come un "letterale" e non come un carattere del linguaggio. Quindi nella sintassi delle regex \. fa match con un "." nel testo, mentre . fa match con qualsiasi carattere.
Per trovare la spiegazione, guardate qui con tutti i dettagli illustrati ottimamente.

Ciò detto sulla ricerca, e trovato il modo per individuare una generica data, potremmo chiederci che possiamo farcene. Mettiamo di voler trasformare tutte le date espresse all'italiana come abbiamovisto nel formato internazionale: anno-mese-giorno. Se ci fate caso, le informazioni necessarie noi le possiamo già "catturare" con la regex delle date che abbiamo costruito: basterebbe prendere i pezzi e riordinarli diversamente. Questo è possibile farlo usando i gruppi, cioè le parentesi tonde. Tutto ciò che viene a trovarsi tra parentesi tonde è un gruppo, e il sistema che si usa lo memorizza permettendoci di riutilizzarlo nella casella SOSTITUISCI. I gruppi sono identificati con la loro posizione della regex partendo da sinistra, quindi il primo gruppo a sinistra ha il numero 1, il secondo il numero 2 e così via. Per riferirci a loro nella casella "sostituisci" usiamo il $, $1 è quello che è stato trovato nel primo gruppo, $2 il secondo e così via.

Riscriviamo la nostra espressione per catturare anche il contenuto dell'anno:
(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/(19[0-9][0-9])
Se noi inseriamo questo in "cerca", e in trova invece inseriamo
$3-$2-$1
Quando clicchiamo "sostituisci tutto" (come si vede nel filmato) il programma effettuerà in modo completamente automatico questo lavoro. Istantaneamente. Gli stiamo di fatto chiedendo: TROVA nel testo una combinazione di caratteri che risponda alla nostra descrizione, e memorizza i caratteri che ricadono nelle posizioni avvolte da parentesi, quindi SOSTITUISCI tutto quello che hai trovato e che hai riconosciuto come data, con la nuova sequenza di caratteri che ottieni prendendo i caratteri che ricadono nel terzo gruppo, seguito da un -, poi il contenuto del secondo gruppo, seguito da un -, poi il contenuto del primo gruppo. In questo modo la sequenza trovata viene ineramente sostituita da una nuova sequenza costruita "al volo" e che varia in base alla stringa riconosciuta. Avremmo ovviamente potuto sostituire qualsiasi cosa alle date, per esmepio delle "xxx" per rimuovere ogni riferimento temporale dal testo! Oppure sostituire la data con $1/$2 per eliminare l'informazione dell'anno.

Un ultimo elemento introdotto a lezione che permette di aggiungere vincoli alle espressioni regolari sono i segnaposti per inizio e fine del paragrafo. Se vogliamo catturare solo le date (o le etichette, come si vede anche nel filmato) che si trovano esattamente all'inizio della riga, è sufficiente inserire un carattere ^ all'inizio della regex:  ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/(19[0-9][0-9]) , questo impone il vincolo che non solo la sequenza deve essere trovata, ma deve trovarsi adiacente all'inizio della riga (se vogliamo riconoscere solo date che partono dalla quinta posizione possiamo sempre fare ^....(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/(19[0-9][0-9]) cioè, dato che il punto è un carattere qualsiasi, partendo da inizio riga contiamo quattro caratteri non meglio specificati, e lì cerchiamo la data.

Un'altra applicazione di cui si è discusso è la rimozione degli spazi vuoti ripetuti in un testo: è sufficiente cercare " +" (uno spazio, seguito da un +) e sostituirlo con uno spazio singolo " ". Per formattare correttamente la punteggiatura è sufficiente usare un gruppo di cattura (tra parentesi quadre il punto è solo un punto, non rappresenta un carattere speciale):
cerca: " *([,.;:]) *"
sosstituisci: "$1 "
In pratica cerca un segno di punteggiatura preceduto e seguito da 0 o più spazi (i vari casi possono essere "ciao,amico" "ciao , amico" "ciao ,amico" "ciao       ,amico" "ciao  ,  amico" ecc.). Dato che l'elemento di punteggiatura è registrato in un gruppo di cattura, nel "sostituisci" possiamo utilizzaro per rimpiazzarlo a se stesso, ma circondato dagli spazi giusti: "ciao, amico".

martedì 7 dicembre 2010

Lezione del 6 dicembre 2010


Lezione del 6 dicembre 2010 PSINFORM from Marco Tonti on Vimeo.

Argomenti della lezione (per un disguido niente audio mi spiace) su OpenOffice Writer:
- Inserimento di interruzioni manuali di pagina
- Margini di paragrafo o dela prima riga del paragrafo
- Stili di paragrafo
- Tabulazioni personali (sinistre, centrate, destre e decimali)
- Inserimento note a piè di pagina
- Inserimento numeri di pagina (ed esclusione dalla prima pagina dalla numerazione, con esempio di ricerca con Google)
- Tabelle
- Esportazione in PDF
- Scorciatoie di tastiera per il copia-incolla (CTRL-C CTRL-V)
- Sistema delle revisioni (memorizzazione dei cambiamenti)
- Ricerca e sostituzione avanzata
- Primi cenni di espressioni regolari

lunedì 6 dicembre 2010

Aggiornamento calendario lezioni

Le lezioni del 9, 13 e 14 dicembre non ci saranno, per via delle lezioni intensive dei prof. Rosa e Valsiner. Le altre lezioni avranno luogo normalmente.

QUESTION TIME

Inserite nei commenti a questo post domande, questioni o argomenti su cose che vi interesserebbe approfondire o cose che non vi sono chiare (anche al di fuori del programma del corso). Cercherò di fare una sintesi di dedicare una lezione alle vostre richieste.

Anticipo lezione del 6 dicembre

In seguito all'indispozione della prof. Venuleo, la lezione di oggi è anticipata alle 15.30 .

venerdì 3 dicembre 2010

Programmi da scaricare per il resto del corso

www.openoffice.org

Per Windows: http://download.services.openoffice.org/files/localized/it/3.2.1/OOo_3.2.1_Win_x86_install-wJRE_it.exe

Per Mac: http://download.services.openoffice.org/files/localized/it/3.2.1/OOo_3.2.1_MacOS_x86_install_it.dmg

Lezione del 3 dicembre 2010 /2

Nella seconda lezione si sono descritti i programmi e i formati più comunemente utilizzati nell'uso quotidiano.

Le prime considerazioni riguardano i file di dati, i file cioè che contengono le informazioni che ci interessa mantenere (la tabella di excel con i voti, l'archivio delle email, le lettere d'amore scritte con Word). Come è in grado il sistema operativo di "riconoscerne" il contenuto in modo da rappresentarli con un'icona che li ricollega al programma con cui sono stati creati o con cui è possibile aprirli? (per esempio i file di word vengono riconosciuti come tali: ma se i file sono solo una sfilza di numeri come è possibile che vengano "riconosciuti" dal sistema?)

Il "segreto" è che i nomi dei file, in windows, possiedono una "estensione" di tre caratteri che li rende riconsudibili al programma appropriato per aprirli. Windows nasconde questa estensione per... semplificare la vita agli utenti (introducendo anche rischi per la sicurezza), ma comuque la mantiene e la usa per avere un'informazione sul contenuto del file. Ogni programma è libero di "scegliere" l'estensione da applicare ai propri file, ma alcune di queste sono diventate ormai consolidate e caratteristiche. Vediamo ne alcune con le diverse caratteristiche.

Formati compressi: ZIP e RAR (o 7z per il 7zip)
Sono le estensioni classiche che rappresentano file che contengono dati compressi. I dati compressi sono dati di qualsiasi genere che vengono ricodificati in modo da fargli occupare uno spazio minore, ma senza perdere nessuna informazione. Per fare un semplice esempio, se il file originale fosse AAABBBCCCCCCC, sarebbe possibile ricodificarlo (in questo esempio in modo molto ingenuo e poco realistico) come 3A3B7C (codifica "run length", di fatto quella usata nei fax). Questa nuova codifica mantiene tutta l'informazione originale (infatti permette di ricostruire il file di partenza) ma è in grado di esprimerla in modo molto più compatto (meno della metà). Ovviamente per poter utilizzare nuovamente i dati è necessario operare il processo inverso per ricostruire il file originario.
Un esempio più realistico è il seguente. Mettiamo di voler comprimere il seguente testo:
CARO AMICO TI SCRIVO PERCHÉ SEI UN AMICO CARO. È immediatamente evidente che c'è una certa ridondanza di informazioni, infatti le parole AMICO e CARO sono ripetute. Cerchiamo un modo per ricodificare questi dati in modo più compatto, in particolare cercando un modo di richiamare dentro al testo pezzi di testo già comparsi. Usiamo la convenzione di scrivere [pos iniz, num car] per indicare che in quel punto si deve sostituire il testo che si trova a partire dalla posizione pos iniz per un certo umero di caratteri. Il testo di esempio diventa quindi: CARO AMICO TI SCRIVO PERCHÉ SEI UN [6,5] [1,4]. In questo esempio particolare il guadagno non è evidente, ma la procedura permette in generale di risparmiare molto spazio nella codifica. Inoltre la procedura potrebbe essere nidificata, mettiamo che nel seguito della lettera si ripetessero le parole AMICO CARO, le si potrebbe sostituire con [35,11] cioè facendo riferimento a una sezione già codificata, e iniziare a "risparmiare" un bel po' di caratteri.
Questo è in generale il funzionamento di programmi tipo winzip (che implementano l'algoritmo Lempel-Ziv) e anche winRAR. Esistono altri algoritmi di compressione (come il metodo di Huffman) ma sono in linea di massima meno efficienti.

Formati immagine: JPG e GIF
Come già spiegato le immagini a livello di base vengono ottenute suddividendo la fotografia in piccoli riquadri, e ogni riquadro (detto pixel) possiede un colore. Registrare tutte quelle informazioni richiede molta memoria, per esempio una foto 800x600 pixel, dove ogni pixel può rappresentare 64000 colori richiede 960 kilobyte. Ci chiediamo quindi se non sia possibile usare una codifica simile a quella illustrata sopra per rendere più efficiente. Data la caratteristica delle immagini, dove molte aree spesso hanno colori uniformi o molto vicini, già una codifica tipo quella run-length è un guadagno.
Con il formato JPG si aggiunge un ulteriore fatto: le compressioni "lossless" (cioè quelle che non perdono informazioni) possono non esere le migliori per le immagini di tipo fotografico, dato che spesso la qualità effettiva richiesta è molto inferiore alla qualità massima: insomma, una foto per quasi tutti gli usi può non essere superdefinita. Il formato JPG mette in pratica questo principio introducendo un formato di compresione di tipo "Lossy" (cioè con perdita).

Formato PDF e PS:
Portable Document Format e PostScript (strettamente imparentati, sviluppati entrambi da Adobe)
Contrariamente ai formati di immagine dove l'immagine viene divisa in puntini, il formato PDF è vettoriale. Questo significa che le aree vengono definite da una serie di piccoli segmenti, e anche le lettere sono costruite in questo modo. Il vantaggio di essere un formato vettoriale è che è possibile ingrandirlo a piacere e mantiene i margini sempre perfettamente definiti.

Formati testuali: TXT e DOC
Normalmente i file con estensione TXT sono quelli che contengono testo semplice, senza formattazioni né altri arricchimenti. Una "a" è una "a" e basta.
Invece i formati di file tipo Word permettono non solo di avere le informazioni delle lettere scritte nel testo, ma anche della particolare formattazione di ogni carattere o parti di testo, oltre alle altre informazioni che si possono inserire come le note o dei grafici o delle immagini. Possiamo immaginare il formato di word descritto con un sistema simile all'xml discusso in una lezione precedente. Dato che abbiamo un testo e delle informazioni "a proposito" del testo, possiamo esprimerle in questo modo:
Il testo Caro amico potremmo pensare di descriverlo come:
<text font="Courier"><italic>Caro</italic></text> <text size="small"><underline>amico</underline></ext>

Lezioni del 3 dicembre 2010 /1

Approfondimento del concetto di sistema operativo. Dato che il computer può eseguire solo un programma alla volta, è necessario immaginare un sistema che ci permetta di utilizzare il computer in un modo più versatile. Il vincolo rimane, ma è possibile strutturare le cose in modo più intelligente per aumentare le possibilità di funzionamento.

Quando accendiamo il computer la memoria è completamente vuota. Per potervi inserire qualcosa è nevessario che le istruzioni del programma siano scritte in un luogo che le possa mantenere nel tempo: questo lugo, come sappiamo, è l'hard disk (ma alcuni vecchi computer avevamo dei piccoli programmi scritti in delle memorie non modificabili inserite direttamente sulla ciruciteria, le note ROM: read only memory). Grazie a un sistema di gestione elementare delle periferiche (che fornisce un funzionamento di base per tastiera, schermo e accesso a memorie di massa come gli hard disk), che si chiama BIOS (Basic Input Output System), il copmuter autonomamente trova sul disco il programma "di avvio" del sistema operativo, lo copia nella memoria e lo esegue. Niente di sostanzialmente diverso da quello che abbiamo visto finora.

Il sistema operativo si occupa di gestire l'interazione tra l'utente e i programmi, e di amministrare l'"ecosistema" dove i programmi "vivono". Dato che i programmi, come abbiamo visto, devono avere (e poter disporre liberamente) di una memoria, il primo "potere speciale" che deve avere un sistema operativo è quello di poter creare delle sotto-aree di memoria "ritagliandole" dalla memoria globale del computer. Da un punto di vista del programma che si trova a funzionare in questa sotto-area, quella è la sua memoria, che comincia da 1 ed è di sua esclusiva competenza. Con questa memoria ci può fare quello che vuole. (ma non può uscirne e invadere la memoria globale o altre aree di memoria: se serve un'aggiunta è il sistema operativo che provvede, automaticamente, a ingrandire lo spazio del programma che ne necessita)

Dentro queste aree di memoria è possibile inserire dei programmi (normalmente estratti dall'hard disk, che rispondono a nomi esotici e strani come "Word", "Outlook Express", "Internet Explorer", "Excel"). Questi programmi si comporteranno né più né meno come quelli "astratti" che abbiamo visto a lezione, lavorando sulla propria memoria privata e facendo cose molto diverse da quelle che facevano i "nostri" programmi, ma facendole usando esattamente le stesse modalità.

Ma dato che in ogni momento solo un programma (anzi: una sola istruzione di un solo programma) può essere in esecuzione, bisogna escogitare una struttra che renda possibile la convivenza contemporanea di più programmi. La soluzione è ingegneristica e non logica: si usa un sistema detto di scheduling. Lo scheduling permette il multitasking, cioè permette di eseguire più compiti contemporaneamente. Per dare l'idea che più programmi possano essere eseguiti allo stesso momento (un programma in esecuzione è detto processo) il sistema operativo reindirizza il flusso delle istruzioni a turno verso uno dei processi in esecuzione, permettendogli di avanzare per alcune frazioni di secondo e quindi fermandolo per poi passare il controllo nuovamente a un altro processo. Quando tutti i processi hanno avuto il loro "pezzettino" di tempo, il ciclo si ripete. In questo modo, a turno, tutti i programmi possono procedere in modo apparentemente parallelo. Il passaggio tra un processo e l'altro è talmente veloce che è impercettibile all'utente che "vede" tutti i processi andare avanti in parallelo e contemporaneamente.


Noi comunichiamo col sistema operativo attraverso l'interfaccia. Fondamentalmente quando facciamo "doppio click" sull'icona di Word, stiamo dando istruzione al sistema operativo di cercare il file che contiene le istruzioni del programma Word sull' hard disk, di creargli una sua area di memoria, di copiare le istruzioni dal disco alla memoria e di farne partire l'esecuzione. A quel punto il processo Word andrà avanti per conto suo, a turno con gli altri processi in funzione. (questo è il motivo per cui più programmi sono "aperti", più è lenta l'esecuzione di ognuno)

Il sistema operativo gestisce e coordina anche le risorse comuni alla macchina sulla quale viene eseguito, per esempio l'accesso al disco, alla stampante e così via.

A questo punto però bisogna rendersi conto che al computer si possono collegare periferiche che hanno modelli, funzionalità, marche, caratteristiche anche molto diverse tra loro. Alcune di queste periferiche potrebbeno non essere nemmeno state previste dai creatori del sistema operativo. Anche per limitarsi solo alle stampanti, ne esistono migliaia di marche e modelli, ognuno con modalità di funzionamento e di gestione del tutto diverse. Come può il sistema operativo gestire delle risorse con caratteristiche così varie? Per fare ciò si utilizzano speciali programmi detti driver. Un driver è un tipo speciale di programma che deve essere scritto secondo una struttura rigida e predefinita. Il programma driver deve possedere alcune funzioni standard (sono le sottoprocedure che abbiamo visto in altre lezioni) con dei parametri standard. Per esempio u driver di stampa dovrà avere funzioni come STAMPA, SALTA_PAGINA, INTERROMPI_STAMPA e così via: sono le funzioni che genericamente ci aspettiamo di poter eseguire con quel particolare hardware. Tutti i driver di stampa devono possedere quelle funzioni. (si dice in gergo che devono possedere una "interfaccia standard": interfaccia in questo caso non è quella visuale di windows, ma una particolare strutura fatta i modo da "incastrarsi" con le aspettative del sistema operativo, una inter-faccia appunto, fatta come il pezzo di un puzzle che si deve adattare alle specifiche del sistema). Le istruzioni contenute in queste funzioni sono invece strettamente dipendenti dall'apparecchio che deve essere pilotato (infatti "driver" significa "pilota"!). Se per esempio una stampante richiede il codice A per saltare pagina, il driver all'interno della procedura SALTA_PAGINA dovrà inviare alla stampante il codice "A". Se una stampante richiede il codice B, il suo driver dovrà sempre avere la procedura SALTA_PAGINA ma all'interno di quest'ultima il codice da inviare sarà "B", e così via. Ogni apparecchio collegato al computer (ma anche quelli all'interno del computer, come la scheda video che permette di mostrare le immagini sullo schermo, o la sched aaudio che permette di riprodurre i suoni) richiede un driver specifico. Molto spesso i sistemi operativi possiedono già una certa collezione di driver per i dispositivi più diffusi, ma nel caso questo driver non sia presente è necessario installarlo col CD fornito con l'apparecchio o scaricarlo dal sito del produttore. Ci si può accorgere di questo fatto quando si inserisce una chiavetta di memoria in una porta USB e compare un messaggio "installazione della periferica in corso": il sistema operativo sta cercando se possiede già il driver di quell'apparecchio e in caso positivo fa in modo di utilizzarlo per interagire con quella periferica.

I driver quindi sono programmi molto speciali, perché diversamente dagli altri che "vivono" una vita propria nella memoria e che in caso di errore possono essere interrotti (col task manager che si apre premendo contemporaneamente ctrl-alt-canc), i driver diventano pezzi del sistema operativo, vengono incorporati in esso. Se nei programmi driver ci sono errori o si verificano malfunzionamenti è l'intero sistema a trovarsi a rischio di instabilità, perché il problema viene a manifestarsi nel cuore stesso del sistema pregiudicandone il buon funzionamento. Questo spiega per quale ragione i driver sono gli elementi dell'ecosistema "computer" a introdurre i maggiori problemi di funzionamento.