Come introdurre la programmazione nelle scuole secondarie?

Assunzione di base

Fare breccia negli studenti è difficile

Punti su cui ci focalizzeremo

  • Scelta del linguaggio
  • Scelta del problema

Scelta del linugaggio

Nella fase iniziale di apprendimento di un linguaggio va ridotta al minimo la quantità di sintassi da introdurre.

  • Nonostante ciò... scaffolding: a seconda del linguaggio scelto vi sono più o meno costrutti sintattici che è impossibile eliminare, pena il non poter compilare ed eseguire un programma.
  • Di conseguenza... leap of faith: non c'è altro modo di introdurre questi costrutti se non chiedendo di impararne a memoria la sintassi e rimandando più avanti nel corso una spiegazione della loro semantica.

Per esempio, in C



#include <stdio.h>

int main() {

    printf("Hello world!\n");

}
          

È necessario

  • includere una libreria (di sistema)
  • dichiarare il prototipo di una funzione
  • utilizzare il tipo int
  • far digerire punto e virgola e sequenza di escape

Peggio ancora, in Java


class HelloWorld {

    public static void main(String args[]) {

        System.out.println("Hello world!");

    }

}
          

È necessario

  • definire una classe
  • dichiarare il prototipo di una funzione pubblica, statica e void
  • che ha come argomento un array di oggetti della classe String
  • che usa la classe System invocando il metodo di un suo campo statico

Non sarebbe meglio...


print('Hello world')
          

Python

  • Introdotto a partire dal 1991
  • Fortemente tipizzato, con type checking a run time
  • Compilato in bytecode ma eseguibile in modo interattivo tramite console
  • Ispirato al Monty Python's Flying Circus

Installazione

  • Si tratta di software floss, disponibile per i principali sistemi operativi (http://www.python.org)
  • Versione corrente: 3.7.X (ma la 2.7 è ancora molto usata)
  • Molte librerie disponibili e molti package manager (per esempio anaconda
  • In particolar modo è suggerita l'installazione di Jupyter, per i più arditi di Jupyterlab

$ conda install jupyter
$ jupyter notebook
            

Una nota sui sistemi operativi

L'uso di sistemi operativi che permettono all'utente di interagire in modo proficuo tramite un terminale è da preferirsi, in quanto permette allo studente

  • di avere più controllo sul file system e in ultima analisi su tutti i file che vengono prodotti nel processo di creazione/compilazione/esecuzione
  • di essere a conoscenza dell'effettivo output di editor, compilatori e altri strumenti
  • di rendersi conto di che cosa succede a un livello relativamente basso del sistema operativo

In particolare le distribuzioni Linux permettono di non gravare sul bilancio degli istituti e tra esse ve ne sono di specificamente pensate per non richiedere hardware altamente performante

Alcuni riferimenti bibliografici

Dati, variabili, espressioni

  • C'è poco di nuovo rispetto a quanto già dovreste conoscere (gli operatori principali sono uguali a C e Java, ** calcola le potenze, // la divisione intera e / quella in virgola mobile)
  • Ma le variabili non vengono dichiarate (niente panico)
  • Sono inoltre possibili espressioni e assegnamenti complessi

x, y, z = 1, 2, 3
a, b = b, a

x < 5 or 10 < x < 20

          

Input e output

Le forme più semplici di input da tastiera e output a video si ottengono tramite le funzioni input e print


x = input("Please enter a number: ")
print("The square of that number is", x*x)
          

Indentazione obbligatoria

Eventuali blocchi di codice non sono identificati da costrutti specifici (tipo begin/end o parentesi graffe), bensì dall'indentazione che deve essere coerente


if x < 5 or 10 < x < 20:
    print "The value is OK."


x = 10
while x >= 0:
    print "x is still not negative."
    x = x-1
          

Tipi di dati

  • La tipizzazione è forte ma controllata a run time, quindi le variabili non vanno dichiarate associandole a un tipo
  • Anche relativamente a come specificare espressioni terminali di un dato tipo non vi sono quasi differenze rispetto a quello che già dovreste conoscere (a parte il fatto che le stringhe si possono delimitare con apici o con doppi apici)

Tipi di dati

Nulla (o quasi) di nuovo.


>>> eta = 24
>>> altezza = 1.78
>>> type(eta)
<type 'int'>
>>> type(altezza)
<type 'float'>
>>> nome = 'Aldo'
>>> len(nome)
5
>>> type(nome)
<type 'str'>
>>> print('Mi chiamo %s, ho %d anni e sono alto %.2f' % (nome, eta, altezza))
Mi chiamo Aldo, ho 24 anni e sono alto 1.78
          

Tuple

Rappresentano un tipo di dati strutturato molto simile agli array, che però è eterogeneo e i cui contenuti non possono essere modificati


>>> (1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
>>> t = (1, 'ciao', 5, 6)
>>> t
(1, 'ciao', 5, 6)
>>> t[0]
1
>>> t[-1]
6
>>> t[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
>>> len(t)
4
>>> t = (1, (2, 3), 4)
>>> t[1][1]
3
>>> t[0] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> s = (1) # Attenti alle liste singoletto!
>>> s
1
>>> s = (1,)
>>> s
(1,)
>>> s = ()
          

Liste

Rappresentano un tipo di dati strutturato simile agli array, che è però eterogeneo e in cui vi è la possibilità di aggiungere o rimuovere elementi a run time


>>> name = ['Cleese', 'John']
>>> x = y = z = 5
>>> l = [[1, 2, 3], [y, z], [[[]]]]
>>> len(l)
3
>>> l[0][1]
2
>>> l[0][1] = 0
>>> l
[[1, 0, 3], [5, 5], [[[]]]]
          

Slicing di liste e tuple

L'operatore : permette di estrarre facilmente sotto-liste e sotto-tuple.



>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[4:6]
[4, 5]
>>> l[:4]
[0, 1, 2, 3]
>>> l[:6]
[0, 1, 2, 3, 4, 5]
>>> l[-2:-3]
[]
>>> l[-3:-2]
[7]
>>> l[4:-1]
[4, 5, 6, 7, 8]
          

Dizionari

Permettono di memorizzare (anche dinamicamente) associazioni tra chiavi e valori.


>>> persona = {'nome': 'Aldo', 'eta': 24, 'altezza': 1.78}
>>> persona
{'altezza': 1.78, 'eta': 24, 'nome': 'Aldo'}
>>> len(persona)
3
>>> persona['nome']
'Aldo'
>>> persona['professione'] = 'professore'
>>> persona['eta'] = 34
>>> persona
{'altezza': 1.78, 'eta': 34, 'professione': 'professore', 'nome': 'Aldo'}
>>> persona[2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 2
>>> persona[2] = 0
>>> persona
{'altezza': 1.78, 2: 0, 'eta': 34, 'professione': 'professore', 'nome': 'Aldo'}
>>> persona[2]
0
          

Moduli e importazione

Le estensioni del linguaggio sono organizzate in moduli o package, da cui si possono importare singole funzioni o interi oggetti


>>> from math import sqrt
>>> sqrt(9)
3.0
>>> import math
>>> math.sqrt(9)
3.0
          

Avete notato che è la prima volta che parliamo di oggetti?

Funzioni

Anche qui nulla di nuovo, a parte la regola dell'indentazione


>>> from math import sqrt
>>> def is_prime(x):
...    d = 2
...    while(d <= sqrt(x)):
...        if x % d == 0:
...            return False
...        d += 1
...    return True
...
>>> is_prime(17)
True
>>> [(n, is_prime(n)) for n in range(2, 20)] # Niente panico!
[(2, True), (3, True), (4, False), (5, True), (6, False),
(7, True), (8, False), (9, False), (10, False), 
(11, True), (12, False), (13, True), (14, False), 
(15, False), (16, False), (17, True), (18, False),
(19, True)]
          

Argomenti opzionali

È possibile dichiarare valori predefiniti per gli argomenti delle funzioni.


>>> import math
>>> 
>>> def log(x, base=10):
...     return math.log(x)/math.log(base)
... 
>>> log(100)
2.0
>>> log(2.71, math.e)
0.9969486348916096
          

Argomenti nominali

Siccome gli argomenti con valori di default hanno un nome, è possibile invocarli senza rispettare l'ordine in cui compaiono nel prototipo della funzione.


>>> def business_card(nome='', cognome='', indirizzo='', citta=''):
...     return '%s %s\n%s %s' % (nome, cognome, indirizzo, citta)
... 
>>> print business_card('Dario', 'Malchiodi', 'Via Celoria 18', 'Milano')
Dario Malchiodi
Via Comelico 39 Milano
>>> print business_card(cognome='Malchiodi', nome='Dario', citta='Milano',
...   indirizzo='Via Celoria 18')
Dario Malchiodi
Via Celoria 18 Milano
          

Classi e oggetti

Finora non ne abbiamo (quasi) parlato, ma è possibile utilizzare il paradigma di programmazione orientato agli oggetti.


>>> from datetime import date
>>> 
>>> d = date(2005, 7, 14)
>>> d.year
2005
>>> d.strftime('%A, %d %B %Y')
'Thursday, 14 July 2005'
          

Eccezioni

La gestione degli errori di esecuzione è basata sul meccanismo delle eccezioni.


>>> def safe_division(a,b):
...     try:
...         return a/b
...     except ZeroDivisionError:
...         return None
... 
>>> safe_division(5, 7)
0
>>> safe_division(9, 0)
          

List comprehension

Ricordate?


>>> [(n, is_prime(n)) for n in range(2, 20)] # Niente panico!
[(2, True), (3, True), (4, False), (5, True), (6, False),
(7, True), (8, False), (9, False), (10, False), 
(11, True), (12, False), (13, True), (14, False), 
(15, False), (16, False), (17, True), (18, False),
(19, True)]
          

Questo costrutto prende il nome di list comprehension e permette di costruire in modo semplice, elegante ed efficiente le liste.


>>> [n for n in range(2, 20) if is_prime(n)] # OK, panico!
[2, 3, 5, 7, 11, 13, 17, 19]
>>> [i+j for i in range(5) for j in range(5)]
[0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8]
>>> [[i+j for i in range(5)] for j in range(5)]
[[0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]]