Introduzione

Cercherò di dare una introudzione formale a cosa sia un linguaggio di programmazione. La più basilare definizione di linguaggio di programmazione è quella di un linguaggio formale che include una serie di istruzioni, utilizzate per produrre un dato risultato. Un linguaggio formale è un insieme di parole dette lessico i cui elementi fondamentali le lettere, sono prese da un alfabeto denotato come $\Sigma$, e che si combinano secondo una sintassi le cui regole sono descritte dalla grammatica del linguaggio. Le grammatiche di un liguaggio possono essere classificate utilizzando la gerarchia di Chomsky, prima però dobbiamo introdurre una definizione di grammatica formale. Una grammatica formale è una quadrupla $\mathcal{G} = \Big(N,\Sigma,P,S\Big)$, dove:

  • $N$ sono i simboli, detti simboli non terminali, che possono essere scritti più volte.
  • $\Sigma$ sono i simboli, detti simboli terminali.
  • $S\in N$ detto assioma è il simbolo non terminale iniziale.
  • $P$ sono le regole di produzione.

Un esempio di grammatica formale l'otteniamo prendendo $N=\{S,N\}$,$\Sigma=\{a,b,c\}$ e come regole di produzione $P$ le seguenti:

  1. $S\to aBSc$,
  2. $S\to abc$,
  3. $Ba \to aB$,
  4. $Bb \to bb$.

il linguaggio costruito con questa grammatica si chiama $\mathcal{L}\Big(N,\Sigma,P,S\Big) =\{a^nb^nc^n | n>0\}$. Siamo ora pronti per definire la gerarchia di Chomsky, come segue:

  1. Grammatiche di tipo 0, con questo termine si intendono tutte le grammatiche che producono un linguaggio comprensibile da una macchina di Turing,
  2. Grammatiche di tipo 1, esse sono chiamate grammatiche dipendenti dal contesto e sono le grammatiche dove le regole di produzione hanno forma: $aAb\to acb$,
  3. Grammatiche di tipo 2, sono chiamate grammatiche libere dal contesto e sono le grammatiche dove le regole di produzione hanno forma: $A\to a$,
  4. Grammatiche di tipo 3, sono chiamate grammatiche regolari e hanno come regole di produzione le seguenti: $A\to a $ e $A\to aB$.

Possiamo dunque osservare che l'esempio precedente di grammatica è una grammatica di tipo 1. E' possibile mostrare che i linguaggi di programmazione appartengono al contesto dei linguaggi con grammatiche di tipo 2.

E' stata proposta l'idea che i linguaggi formali, in particolare i linguaggi di programmazione, sono prodotto delle stesse facoltà di linguaggio che servono a processare i linguaggi naturali. Questo suggerisce che i linguaggi di programmazione possono essere utilizzati per testare ipotesi sulla natura dei linguaggi che non possono essere tesate con le limitazioni imposte dai linguaggi naturali sulle grammatiche. In particolare risulta interessante studiare il problema della competenza nel caso dei linguaggi di programmazione. Il problema della competenza per i linguaggi naturali è stato introdotto da Chomsky ed è differente nel caso dei linguaggi di programmazione. Maggiori infromazioni si possono trovare in [2].

Dati e Strutture

Per prima cosa introdurremo la i dati, ovvero ciò che andremo a manipolare con l'insieme delle istruzioni fornite dal Python. Un dato è il risultato di una qualsiasi espressione valutabile dal nostro linguaggio, inclusa l'operazione nulla. Esempi di dati generati a partire dall'operazione nulla sono i seguenti:

In [47]:
1567
Out[47]:
1567
In [48]:
"Ciao Mondo"
Out[48]:
'Ciao Mondo'
In [49]:
1567.0
Out[49]:
1567.0

I moderni linguaggi di programmazione , tra cui il Python, ricorrono alla tipizzazione dei dati al fine di prevenire errori di natura semantica. In particolare il Python utilizza una tipizzazione dinamica, ovvero quando introduciamo un dato il linguaggi di programmazione gli assegna automaticamente un tipo, tra quelli primitivi, i.e: boolean, int, float, complex, str, list, tuple e range (maggiori informazioni si possono trovare in [1]). Possiamo verificare quale è il tipo di un dato utilizzando il comando type.

In [50]:
type(1567)
Out[50]:
int
In [51]:
type("Ciao Mondo")
Out[51]:
str
In [52]:
type(1567.0)
Out[52]:
float

segue una breve introduzione a ciascuna delle tipologie di dato primitivo, fatta eccezione che per i dati di tipo range che approfondiamo nella prossima lezione.

Boleani

Il primo tipo di dato che introduciamo sono i boleani,ovvero la nozione di vero o falso. In particolare con la tipizzazione dei Boleani si indicano dati che possono assumere solo due valori, il valore 0 epresso mediante l'istruzione False o il valore 1 epresso mediante l'istruzione True. Una definizione più appropriata di Booleano sarebbe quella di un dato che appartiene a $(B,+,*)$, un reticolato completo e distributivo. I Booleani vengono rappresentati con la parola bool, come possiamo osservare eseguendo l'istruzione type:

In [53]:
type(True)
Out[53]:
bool
In [54]:
type(False)
Out[54]:
bool

Come già osservato vi sono due operazione naturali sui Boleani, che nella definizione del reticolato abbiamo denotato con i simboli + e *. Queste due operazioni corrispondono rispettivamente alla congiunzione logica e alla disgiunzione logica. Mediante l'utilizzo del comando Python and costruiamo ora la tabella di vertà per la congiunzione logica:

In [55]:
True and True
Out[55]:
True
In [56]:
True and False
Out[56]:
False
In [57]:
False and True
Out[57]:
False
In [58]:
False and False
Out[58]:
False

Abbiamo dunque trovato la ben nota tabella di verità per la congiunzione logica, ovvero:

a b a and b
V V V
V F F
F V F
F F F

ESERCIZIO Costruire la tabella di verità per la disgiuzione logica, facendo uso del comando or.

Un altra istruzione utile quando si lavora con i Boleani è la negazione, che in Python è rappesenata dall' istuzione not:

In [59]:
not True
Out[59]:
False
In [60]:
not False
Out[60]:
True

Interi

Gli interi in Python hanno un tipo dedicato, che viene indicato con la parola int. A differenza di altri linguaggi di programmazione, come il C++, il Python non differenzia gli interi dotati di segno dagli interi senza segno, come possiamo verificare utilizzando il comando type:

In [61]:
type(16)
Out[61]:
int
In [62]:
type(-4)
Out[62]:
int

Il Python implementa tutte le principali operazioni tra numeri interi, ovvero la somma,il prodotto,la sottrazione e la divisione in particolare possiamo esprimere queste operazioni per mezzo delle rispettive istruzioni: +,*,-,//.

In [63]:
1+0
Out[63]:
1
In [64]:
5*1
Out[64]:
5
In [65]:
7-1
Out[65]:
6
In [66]:
15//2
Out[66]:
7

Per quanto riguardo l'operazione tra interi è importante osservare che possiamo anche calcolare il resto della divisione mediante l'utilizzo dell'istruzione %.

In [67]:
15%2
Out[67]:
1

Float

Con il termine float, si identifica la tipizzazione dei numeri decimali in Python. La questione della rappresentazione dei numeri reali in un calcolatore è un argomento alquanto delicato, a cui cercherò di dare un breve accenno introducendo la rappresentazione in virgola mobile. Supponiamo di voler rappresentare il numero $\pi$ in un calcolatore, rappresenteremo un approsimazione di $\pi$ utilizzando un numero di cifre significative e un esponenziale a base fissata. $$ \pi = s\cdot m^p $$ la quantità $s$ prende il nome di significando, $m$ quello di mantissa e chiameremo $p$ esponente. In particolare sarà fissato il numero di bits che potremo utilizzare per rappresentare sia $s$ che $m$. Un esempio di rappresentazione in virgola mobile è lo standard IEEE 754, dove abbiamo due tipologie di float:

Nome Bits di $s$ Bits di $p$ Mantissa $m$
float32 24 8 2
float64 53 11 2

In Python non c'è alcuna distinzione tra i dati di tipo float32 e float64, per verificare questo introduciamo un float e usiamo l'istruzione type:

In [68]:
type(3.14)
Out[68]:
float

Possiamo eseguire tutte le classiche operazione che abbiamo introdotto tra i numeri interi anche sui float, fatta ecezzione per // e %.

Esercizio Si calcoli utilizzando la console Python l'area e la circonferenza di un cerchio di raggio $R$ pari a $2.3$, utilizzando come approsimazione di $\pi$ il numero reale 3.14. L'elevamento a potenza si ottiene mediante l'istruzione **

Complex

Un altra tipologia di dato particolarmente, particolarmente utile ai matematici e ai fisici, sono i numeri complessi. Quest'ultimi vengono indicati in Python con la parola complex, come si può verificare utilizzando l'istruzione type:

In [69]:
type(1.41+1.41j)
Out[69]:
complex

Per lavorare con i dati di tipo complex abbiamo una serie di istruzioni oltre alle operazioni basilari introdotte per i numeri interi e i float. Possiamo calcolare la parte immaginaria, la parte reale e il modulo dei numeri complessi utilizzando rispettivamente le istruzioni imag, real e abs:

In [70]:
(1.40+1.42j).imag
Out[70]:
1.42
In [71]:
(1.40+1.42j).imag
Out[71]:
1.42
In [72]:
abs(1.41+1.41j)
Out[72]:
1.994041122946064

Esercizio Si sfruttino le proprietà dei numeri complessi per calcolare il punto sul piano complesso che corrisponde ad una rotazione di $30^\circ$ del punto $1.41+1.41i$.

Out[73]:
[<matplotlib.lines.Line2D at 0x7fc09beaee50>]

String

Un particolare tipo di dati sono le stringhe esse rappresentano le parole e si denotano in Python con la parola str, come possiamo verificare utilizzando il comando type.

In [74]:
type('Ciao Mondo')
Out[74]:
str

La manipolazione delle stringhe può essere molto complicata sopratutto in linguaggi che non hanno un allocazione automatica della memoria come il C. Vediamo in Python alcune istruzioni per la manipolazione delle stringhe. Possiamo calcolare il numero di lettere che compongono una stringa utilizzando l'istruzione len:

In [75]:
len('Ciao Mondo')
Out[75]:
10

anche gli spazi vengono conteggiati come lettere, se vogliamo evitre questa cosa possiamo filtrare la stringa in modo tale rimuovere gli spazie e riconteggiarne la lunghezza:

In [76]:
''.join(filter(lambda c : c not in [' ' ],'Ciao Mondo'))
Out[76]:
'CiaoMondo'
In [77]:
len(''.join(filter(lambda c : c not in [' ' ],'Ciao Mondo')))
Out[77]:
9

Possiamo concatenare due stringhe utilizzando il comando di somma, come possiamo osservare:

In [78]:
'Ciao'+' '+'Mondo'
Out[78]:
'Ciao Mondo'

Possiamo anche modificare una specifica lettera di una stringa e aggiungere una lettera in uno specifico punto di una stringa trasformandola in una lsita, per mezzo dell'istruzione list, vedremo meglio come lavorare con le liste nella prossima sezione.

List

La struttura dati di tipo lista è un elenco di dati di una delle tipologie primitive. Per esempio possiamo costruire un elenco di parole:

In [79]:
['Ciao', 'Mondo', 'Sono', 'Bolo']
Out[79]:
['Ciao', 'Mondo', 'Sono', 'Bolo']

a differenza delle array del linguaggio C, in Python la lista può essere composta da dati di tipo divero:

In [80]:
['Ciao','Sono','Alto',180,'cm']
Out[80]:
['Ciao', 'Sono', 'Alto', 180, 'cm']

Possiamo anche costruire una lista di int,float,complex e addirittura di liste stesse. Possiamo infatti costruire una lista di liste:

In [81]:
[[1,2],[3,4]]
Out[81]:
[[1, 2], [3, 4]]

Possiamo accedere agli elementi di una lista indicandone la posizione e allo stesso modo possiamo anche modificare lo specifico elemento di una lista specificando la posizione del sudetto elemento.

In [82]:
['Ciao','Mondo','Sono','Bolo'][3]
Out[82]:
'Bolo'
In [83]:
'CiaoMondo'[0:4]+' '+'CiaoMondo'[4:9]
Out[83]:
'Ciao Mondo'

Nel caso di liste di liste possiamo concatenare l'operazione di accesso alle liste, come segue:

In [84]:
[['Mario',195,'M'],['Roberto',180,'M'],['Anita',170,'F']][0]
Out[84]:
['Mario', 195, 'M']
In [85]:
[['Mario',195,'M'],['Roberto',180,'M'],['Anita',170,'F']][0][1]
Out[85]:
195

Mediante l'istruzione append possiamo aggiungere facilmente elementi alla fine dell'array:

In [86]:
['Ciao','Mondo','Sono'].append('Perry')

Si può usare l'istruzione + anche per concatenare le liste, ie:

In [87]:
['Ciao','Mondo','Sono']+['Perry']
Out[87]:
['Ciao', 'Mondo', 'Sono', 'Perry']

Variabili

Introduciamo ora il concetto di variabile, una variabile si può immaginare come una scatola su cui imponiamo un etichetta (il nome delle variabile) e all'interno della quale possiamo immagazzinare un dato. La sintassi per dichiarare una variabile è la seguente:

In [88]:
A = ['Ciao','Mondo','Sono']

abbiamo appena dichiarato una variabile di nome A all'interno della quale mettiamo la lista:

In [89]:
['Ciao','Mondo','Sono']
Out[89]:
['Ciao', 'Mondo', 'Sono']

possiamo utilizare tutti le istruzioni delle tipologie di dato che abbiamo visto precedentemente se la variabile ha all'interno dati di una delle tipologie precedenti.

In [90]:
A.append('Perry')
In [91]:
A
Out[91]:
['Ciao', 'Mondo', 'Sono', 'Perry']

Un modo per stampare a schermo il contenuto di una variabile è utilizzare l'istruzione print:

In [92]:
print(A)
['Ciao', 'Mondo', 'Sono', 'Perry']

Conversione Tra Dati

Vediamo ora le principali istruzione per la conversione dei dati da una tipologia ad un altra:

In [93]:
type('3')
Out[93]:
str
In [94]:
type(int('3'))
Out[94]:
int
In [95]:
type(3)
Out[95]:
int
In [96]:
type(str(3))
Out[96]:
str
In [97]:
type('3.14')
Out[97]:
str
In [98]:
type(float('3.14'))
Out[98]:
float

Riferimenti

[1] Built-in Types section in Python 3 documentation, https://docs.python.org/3/library/stdtypes.html

[2] Vigo, E.M., 2015. On The Need of A Linguistic Theory of Programming Languages.

In [ ]: