Lezione 6 di 7 · 18 min di lettura
Stampare come si deve
printf e i segnaposto: mettere i valori dentro le frasi, scegliere le cifre decimali, allineare le colonne.
Il problema
Finora per stampare “Ada ha 36 anni” avremmo dovuto fare tre stampe separate, e il risultato sarebbe finito su tre righe. Non va bene. Vogliamo comporre una frase con dentro dei valori.
La soluzione è io::printfn, la versione “formattata” di printn (la f sta per format):
import std::io;
fn void main()
{
String name = "Ada";
int age = 36;
io::printfn("My name is %s and I am %d years old.", name, age);
}My name is Ada and I am 36 years old.Il primo argomento è la stringa di formato: una frase con dei buchi. I buchi sono i segnaposto che iniziano con %. Gli argomenti successivi riempiono i buchi, in ordine: %s prende name, %d prende age.
Ecco la famiglia completa delle funzioni di stampa:
| Funzione | Formattata? | Va a capo? |
|---|---|---|
io::print(x) | no | no |
io::printn(x) | no | sì |
io::printf("...", a, b) | sì | no |
io::printfn("...", a, b) | sì | sì |
I segnaposto principali
| Segnaposto | Cosa stampa | Esempio | Risultato |
|---|---|---|---|
%s | qualsiasi cosa, nella sua forma naturale | %s con "ciao", 42, true | ciao, 42, true |
%d | un numero intero | %d con 42 | 42 |
%f | un decimale con 6 cifre dopo la virgola | %f con 1.65 | 1.650000 |
%.2f | un decimale con 2 cifre (o quante ne scrivi) | %.2f con 1.65 | 1.65 |
%c | un char come carattere | %c con 'Z' | Z |
%x | un intero in esadecimale | %x con 255 | ff |
%b | un intero in binario | %b con 5 | 101 |
%% | il simbolo % letterale | "100%%" | 100% |
Il caso dei decimali
Il segnaposto più importante da padroneggiare è %.Nf, perché 1.650000 non è quasi mai quello che vuoi mostrare:
double price = 19.99;
double discount = 0.15;
double final_price = price * (1 - discount);
io::printfn("Final price: %f", final_price);
io::printfn("Final price: %.2f", final_price);
io::printfn("Discount of %.0f%%", discount * 100);Final price: 16.991500
Final price: 16.99
Discount of 15%Il numero dopo il punto è quante cifre decimali vuoi. %.0f arrotonda all’intero. E nota %% per ottenere un % vero, perché un % da solo sarebbe letto come l’inizio di un segnaposto.
Dettagli nerd Perché 1,65 «non esiste»?
Perché un double è una frazione binaria: può rappresentare esattamente solo le somme di potenze di 2 (1/2, 1/4, 1/8…). 1,65 non si può scrivere così con un numero finito di cifre, come 1/3 in base 10, e il double si ferma alla migliore approssimazione che sta nei suoi 52 bit di mantissa (lo abbiamo visto nella lezione 4 con 0.1). Per i soldi, la soluzione classica è lavorare in centesimi con un intero: int cents = 165; è esatto, sempre.
Quiz
Cosa stampa io::printfn("%d%%", 7 / 2);?
Larghezza e allineamento
Tra % e la lettera puoi mettere una larghezza minima: %5d stampa il numero in almeno 5 caratteri, riempiendo di spazi a sinistra. Con un - riempie a destra, con uno 0 riempie di zeri. Serve per fare le colonne:
io::printfn("[%5d]", 42);
io::printfn("[%-5d]", 42);
io::printfn("[%05d]", 42);
io::printfn("[%8.2f]", 3.14159);
io::printfn("[%-8s]", "ab");[ 42]
[42 ]
[00042]
[ 3.14]
[ab ]Le parentesi quadre sono nel testo solo per far vedere gli spazi. Con questi strumenti puoi fare una tabella ordinata:
import std::io;
fn void main()
{
io::printfn("%-10s %5s %8s", "Prodotto", "Qta", "Prezzo");
io::printfn("%-10s %5d %8.2f", "Caffe", 2, 1.20);
io::printfn("%-10s %5d %8.2f", "Cornetto", 3, 1.50);
io::printfn("%-10s %5d %8.2f", "Spremuta", 1, 3.00);
}Prodotto Qta Prezzo
Caffe 2 1.20
Cornetto 3 1.50
Spremuta 1 3.00Costruire una riga a pezzi
printf (senza n) non va a capo, e questo permette di costruire una riga in più passaggi, per esempio dentro un ciclo (li vedremo nel Modulo 2):
io::printf("%d, ", 1);
io::printf("%d, ", 2);
io::printfn("%d.", 3);1, 2, 3.Le sequenze di escape, di nuovo
Dentro la stringa di formato valgono tutte le sequenze della lezione 4. La più utile con le tabelle è \t, la tabulazione, che allinea “a scatti” di 8 caratteri:
io::printfn("Name:\tAda");
io::printfn("Age:\t36");Name: Ada
Age: 36È più grezza della larghezza esplicita (%-10s), ma per due righe al volo va benissimo.
Dettagli nerd Perché proprio 8 caratteri?
È un’eredità delle macchine da scrivere e delle telescriventi: il tasto tabulatore faceva scorrere il carrello fino al fermo successivo, e i terminali hanno adottato fermi fissi ogni 8 colonne. Il carattere \t è un singolo byte (il numero 9 nella tabella ASCII) e non contiene nessuno spazio: è il terminale che, ricevendolo, sposta il cursore al fermo successivo. Per questo Name: (5 caratteri) e Age: (4) fanno partire il valore dalla stessa colonna, la nona. Molti editor invece mostrano la tabulazione larga 4: stesso byte, disegno diverso.
Quiz
Quale chiamata stampa esattamente Total: 6.90 euro se double t = 6.9;?
Esercizio · sul tuo computer
Lo scontrino
Scrivi receipt.c3. Dichiara due costanti, COFFEE_PRICE = 1.20 e CROISSANT_PRICE = 1.50, entrambe double. Poi le quantità: int coffees = 2; e int croissants = 3;. Calcola il costo dei caffè, quello dei cornetti, il totale, e il resto se il cliente paga con 10 euro.
Stampa esattamente queste quattro righe (attenzione a spazi, x, = e alle due cifre decimali):
Coffee x2 = 2.40
Croissants x3 = 4.50
Total: 6.90 euro
Paid with 10 euro, change: 3.10Le quantità devono venire dalle variabili, non scritte a mano nella stringa. Riporta l’output.
Mostra una soluzione (prima prova da solo!)
import std::io;
const double COFFEE_PRICE = 1.20;
const double CROISSANT_PRICE = 1.50;
fn void main()
{
int coffees = 2;
int croissants = 3;
int paid = 10;
double coffee_cost = coffees * COFFEE_PRICE;
double croissant_cost = croissants * CROISSANT_PRICE;
double total = coffee_cost + croissant_cost;
double change = paid - total;
io::printfn("Coffee x%d = %.2f", coffees, coffee_cost);
io::printfn("Croissants x%d = %.2f", croissants, croissant_cost);
io::printfn("Total: %.2f euro", total);
io::printfn("Paid with %d euro, change: %.2f", paid, change);
}Ricapitolando
io::printfn("frase con %s e %d", a, b)riempie i segnaposto in ordine.%sstampa qualsiasi cosa;%dqualsiasi intero;%.2fun decimale con 2 cifre;%cun carattere;%%un%.- Larghezza:
%5dallinea a destra,%-5da sinistra,%05driempie di zeri. - Il compilatore controlla che argomenti e segnaposto corrispondano.
printfsenzannon va a capo: utile per costruire righe a pezzi.
Sei arrivato all’ultima lezione del modulo: un riepilogo, una tabella da tenere a portata di mano e una sfida che mette insieme tutto.