Riepilogo
Questa lezione apre la quinta unità del corso, dedicata alle funzioni. Spiega a che cosa serve l’unità: insegnare a scomporre un problema in parti con un nome, ciascuna con un’interfaccia e un contratto, e a risolvere certi problemi riducendoli a un’istanza più piccola di sé stessi, per ricorsione; presenta le sue quattro lezioni e il filo che le unisce; ricorda ciò che l’unità dà per scontato, soprattutto i cicli e i loro invarianti dell’unità 4; formula il problema che l’unità risolve, quello di un programma che ripete lo stesso calcolo copiato e che, per una svista nella copia, risponde male senza che il compilatore dica nulla; anticipa il contratto (la precondizione e la postcondizione) come la forma con cui l’unità specificherà ogni parte; e mostra dove conduce, nelle unità successive e nel corso Programmazione in C++ II.
Obiettivi di apprendimento
- Descrivere la sequenza dei contenuti dell’unità e spiegare che cosa ogni lezione apporta alla successiva.
- Ricordare gli algoritmi dell’unità 4 (la traccia di un ciclo, il suo invariante e la sua variante) e ripassarli se occorre.
- Anticipare, con un esempio, l’idea di contratto: ciò che una parte del programma esige da chi la usa e ciò che gli garantisce in cambio.
- Riconoscere che cosa ci si aspetta di saper fare al termine dell’unità, e distinguerlo da ciò che si imparerà nelle unità successive.
Valutazione d’ingresso
Prima di percorrere l’unità conviene misurare il punto di partenza. Questa valutazione è un’autovalutazione: non ha voto, e il suo unico scopo è orientare lo studio. Le prime cinque domande verificano ciò che l’unità dà per scontato: la traccia di un ciclo, il suo invariante, la precondizione di una specifica, la copia di un valore nell’inizializzazione e la ricerca, dentro un programma dell’unità 4, di un calcolo con ingressi e uscite propri. Le cinque successive toccano, nell’ordine delle lezioni, le idee centrali dell’unità: lo stack delle chiamate, il passaggio di un argomento, la separazione tra il calcolo e la presentazione, la definizione ricorsiva del fattoriale e l’esecuzione di una funzione che chiama sé stessa. Nessuno è tenuto a indovinarle già, e in alcune di esse «non lo so ancora» è una risposta onesta: ogni risposta, giusta o no, spiega il punto e nomina la lezione che lo tratta. Le stesse abilità si misurano di nuovo alla fine, nella lezione di sintesi.
Prima di iniziare l’unità, misura il tuo punto di partenza. È un’autovalutazione breve: non riceve voto e il risultato resta solo nel tuo browser.
Domanda 1Dalle unità precedentiChe cosa scrive
int n{305}; int c{1}; while (n >= 10) { n = n / 10; ++c; } std::println("{}", c);?Conta le iterazioni e dimentica checparte da 1.Fa un’iterazione di troppo: con la condizionen >= 10è già falsa.Confonde il contatore con il valore dindopo la prima iterazione.La traccia dà e : il corpo si esegue due volte, e con la condizione fallisce. Il ciclo conta le cifre di un naturale; la lezione Funzioni lo racchiude in una funzione con nome e contratto, e la lezione Ricorsione lo scrive senza ciclo.Ripassare: Cicli
Domanda 2Dalle unità precedentiNel ciclo
int total{0}; int k{1}; while (k <= n) { total += k; ++k; }, con , quale relazione vale ogni volta che si valuta la condizione?Vale a metà dell’iterazione, dopo la somma e prima dell’incremento, ma non quando si valuta la condizione.Vale solo alla fine: è la postcondizione, non l’invariante.È la condizione del ciclo, ed è falsa proprio all’uscita; un invariante vale anche allora.Vale all’inizio, con la somma vuota e ; e se vale prima di un’iterazione, vale dopo, perché l’iterazione somma e poi lo incrementa. All’uscita, , e l’invariante dà la somma da 1 a . La lezione Ricorsione giustifica le sue funzioni con lo stesso ragionamento per induzione.Ripassare: Progettare cicli corretti
Domanda 3Dalle unità precedentiLa specifica della divisione dice: «dati interi e , ottenere e con e ». Qual è la sua precondizione?
Confonde la precondizione con la postcondizione: questo è ciò che il risultato soddisfa, non ciò che si esige dai dati.È una condizione sull’uscita, non sull’ingresso.Con non esiste alcun con : il problema non ha soluzione, e la precondizione lo esclude.La precondizione è ciò che si esige dai dati perché il problema abbia soluzione; la postcondizione, ciò che il risultato soddisfa. L’unità converte questa coppia nel contratto di ogni funzione: la precondizione obbliga chi chiama, e la postcondizione, la funzione.Ripassare: Dal problema all’algoritmo
Domanda 4Dalle unità precedentiDopo
int a{3}; int b{a}; b = 7;, quanto valea?Crede chebearestino unite per sempre; l’inizializzazione copia il valore, non la cella.Confonde la copia con una variabile non inizializzata:aha ricevuto 3 e nulla l’ha cambiato.L’inizializzazioneint b{a}copia nella cella dibil valore che avevaa; dopo, le due celle sono indipendenti. La lezione Parametri e ambito di visibilità mostra che un parametro si inizializza allo stesso modo, con una copia dell’argomento, e insegna l’altra possibilità: un secondo nome per la stessa cella.Ripassare: Variabili e memoria
Domanda 5Dalle unità precedentiIl programma della lezione 4.4 che scrive i primi minori di 100 ha un ciclo che percorre i candidati e, al suo interno, un altro che prova i loro divisori. Quale parte è un calcolo con ingressi e uscite propri, che converrebbe nominare?
Confonde l’ingresso e l’uscita del programma intero con quelli di un calcolo interno.È un’istruzione, non un calcolo con un senso proprio: non ha un ingresso e un risultato che qualcuno voglia usare.Confonde un dato con un compito: la variabile conserva il risultato del calcolo, ma non è il calcolo.Il ciclo interno non dipende da nient’altro che dal candidato, e il suo risultato è un sì o un no. Dargli un nome permette di leggere il ciclo esterno come la sua specifica: «per ogni candidato, se è primo, scriverlo». La lezione Progettare con le funzioni riscrive così quel programma.Ripassare: Algoritmi numerici
Domanda 6Un programma comincia in
main;mainchiama una funzioneg, egchiama un’altra funzioneh. Mentrehsi esegue, quali chiamate sono in corso?Crede che chi chiama scompaia al momento della chiamata; invece aspetta, per continuare dove si è fermato.Crede chegsia terminata chiamandoh; terminerà quandohle restituirà il controllo.È una risposta onesta: la lezione 5.1 lo spiega con una figura dello stack.Domanda 7Con la funzione
int f(int x) { x = x + 1; return x; }, che cosa scriveint a{3}; int b{f(a)}; std::println("{} {}", a, b);?Crede che la funzione modifichi l’argomento; per impostazione predefinita, ne riceve una copia.Crede che il cambiamento dixnon raggiunga nemmeno il valore restituito.Crede che un parametro non possa essere modificato; può, a meno che non sia dichiaratoconst.Il parametroxè una variabile difche si inizializza con una copia dell’argomento: la funzione cambia la copia e restituisce 4, eaconserva il suo 3. È il passaggio per valore, il modo predefinito di C++.Ripassare: Parametri e ambito di visibilità
Domanda 8Un programma legge una data e deve scrivere il suo giorno della settimana. Quale scomposizione separa il calcolo dalla presentazione?
Mescola due compiti: ciò che è stato calcolato non può essere riusato né confrontato con un valore atteso.Separa i compiti, ma li mette in comunicazione con un effetto nascosto: chi legge la chiamata non vede da dove viene il dato.È una risposta onesta: la lezione 5.3 insegna a decidere quali funzioni scrivere.Così il calcolo si testa con numeri, senza guardare lo schermo, e può servire a un altro programma che scriva il risultato in un altro modo. La lezione 5.3 chiama questo criterio «una funzione, un compito».Ripassare: Progettare con le funzioni
Domanda 9Quale delle definizioni seguenti definisce per ogni naturale ?
Senza caso base, la definizione non si ferma mai: rimanderebbe a , e così senza fine.Definisce bene per , ma lascia indefinito , che vale 1.Definisce a partire da sé stesso: la definizione è circolare e non riduce il problema.Un caso base che non dipende da nulla e un caso ricorsivo che riduce a : per calcolare si scende fino a e si moltiplica al ritorno. La lezione Ricorsione traduce questa definizione in una funzione che chiama sé stessa.Ripassare: Ricorsione
Domanda 10Con
int f(int n) { if (n == 0) { return 0; } return n + f(n - 1); }, che cosa scrivestd::println("{}", f(3));?Crede che si esegua una sola chiamata, o confonde l’argomento con il risultato.Crede che ilreturn 0del caso base sia il risultato di tutta la catena; è solo quello dell’ultima chiamata.Il nome della funzione è noto fin dalla sua intestazione, e può essere usato nel suo stesso corpo.f(3)aspettaf(2), che aspettaf(1), che aspettaf(0); questa restituisce 0, e al ritorno si sommano 1, 2 e 3. La funzione calcola , la stessa somma del ciclo della seconda domanda.Ripassare: Ricorsione
Caricamento del contenuto…