|
Alcuni programmi di
esempio Utilizzando i costrutti introdotti fino ad ora,
proviamo a realizzare un programma che calcoli la somma
dei primi 100 numeri interi:
public class SommaNumeri {
public static void main(String
argv[]) { int i
=0; int somma =
0;
while(i <= 100) {
somma = somma +
i; i = i +
1; } System.out.print("La
somma dei primi 100 numeri e'
"); System.out.println(somma); } }
In questo
esempio vengono utilizzate due variabili: 'i' e 'somma'.
La prima variabile viene inizializzata a zero, e viene
incrementata di un'unità ad ogni iterazione del ciclo.
La seconda variabile, anch'essa inizializzata a zero,
serve ad accumulare il valore cercato: dopo il primo
ciclo essa vale 1, dopo il secondo 3, dopo il terzo 6 e
così via. Il ciclo termina non appena i supera il valore
100. Si vuole ora
modificare il programma in modo che calcoli, oltre alla
somma dei primi 100 interi, la somma dei primi 50 numeri
pari. Un modo per distinguere i numeri pari da quelli
dispari è quello di considerare il resto della divisione
per due: se è uguale a 0, il numero è pari, in caso
contrario è dispari.
public class SommaNumeriPari {
public static void main(String
argv[]) { int i
=0; int somma =
0; int sommaPari =
0; int resto =
0;
while(i <= 100) {
somma = somma +
i; resto = i %
2;
if(resto
== 0)
{ sommaPari
= sommaPari +
i; } i = i +
1; } System.out.print("La
somma dei primi 100 numeri e'
"); System.out.println(somma); System.out.print("La
somma dei primi 100 numeri pari e'
"); System.out.println(sommaPari); } }
Ad ogni ciclo
viene calcolato il resto della divisione per due del
numero i; se è uguale a zero, il valore di 'i' viene
sommato a quello della variabile 'sommaPari':
if(resto
== 0) { sommaPari = sommaPari +
i; }
Si vuole ora
mettere in atto un'ulteriore modifica al programma di
esempio, per fare in modo che calcoli, oltre alla somma
dei primi 100 interi e dei primi 50 numeri pari, la
somma dei primi 50 numeri dispari. Ovviamente dobbiamo
inserire una nuova variabile, 'sommaDispari', per
accumulare il risultato; quindi dobbiamo aggiungere la
clausola 'else' all'istruzione 'if' introdotta
precedentemente:
if(resto
== 0) { sommaPari = sommaPari
+ i; } else { sommaDispari =
sommaDispari + i; }
Le due
istruzioni verranno eseguite alternativamente: nei cicli
pari viene eseguita la prima, nei cicli dispari la
seconda. Ecco il codice completo
dell'esempio:
public class SommaNumeriPariEDispari {
public static void main(String
argv[]) { int i
=0; int somma =
0; int sommaPari =
0; int sommaDispari =
0; int resto =
0;
while(i <= 100)
{ somma = somma +
i; resto = i %
2;
if(resto ==
0)
{ sommaPari
= sommaPari +
i; } else
{ sommaDispari
= sommaDispari +
i; }
i = i +
1; } System.out.print("La
somma dei primi 100 numeri e'
"); System.out.println(somma);
System.out.print("La somma
dei primi 100 numeri pari e'
"); System.out.println(sommaPari); System.out.print("La
somma dei primi 100 numeri dispari e'
"); System.out.println(sommaDispari); } }
Cicli while nidificati All'interno di un ciclo while possiamo inserire
qualunque istruzione, compreso un altro ciclo while: in
questo caso il ciclo più interno verrà rieseguito per
intero ad ogni passo del ciclo più esterno. Il programma
seguente calcola la tabellina del 10, e la stampa sullo
schermo:
public class Tabelline {
public static void main(String
argv[]) { int i =
0; int j = 0;
while(i <= 10)
{ while(j <=
10)
{ int
prodotto =
i*j; System.out.print(prodotto); System.out.print("\t"); j
= j +
1; } System.out.println(); i
= i + 1; j =
0; } } }
Il ciclo più
esterno ha il compito di stampare le righe: ad ogni
passo, esso avvia un altro ciclo in modo da stampare un
valore per ogni colonna. Tra un valore e l'altro viene
stampato un carattere di tabulazione, in modo da
ottenere una formattazione precisa dei dati.
L'uso dei
cicli nidificati è una tecnica di programmazione
importantissima, che presenta delle ovvie difficoltà a
chi si avvicina alla programmazione per la prima volta.
Il tema verrà ripreso e sviluppato nei prossimi
capitoli: per il momento è sufficiente aver chiara la
possibilità di comporre qualunque tipo di combinazione
di istruzioni, ricorrendo addirittura a strutture
nidificate.
Quanti programmi posso scrivere con le nozioni
appena apprese? I costrutti
descritti fino ad ora sono solo una piccola parte di
quelli offerti dal linguaggio Java, tuttavia essi
permettono di realizzare un gran numero di programmi.
Dopo aver preso confidenza con gli esempi, si può
tentare di realizzare qualcosa di diverso: con un po' di
fantasia è possibile individuare migliaia di
possibilità. Ci si può domandare quali siano i limiti di
questo mini linguaggio, così semplice e primitivo.
Quanti programmi è possibile scrivere? Che tipo di
applicazioni si possono realizzare? La risposta, per
certi versi sconcertante, è che questo piccolo
linguaggio permette di realizzare qualunque programma si
desideri, compreso Word, Excel, un compilatore Java o il
vostro sistema operativo preferito.
Nel 1966 due
matematici italiani, Corrado Bohm e Giuseppe Jacopini,
dimostrarono formalmente che qualunque programma per
calcolatore poteva essere riscritto usando un linguaggio
dotato solamente di variabili intere, assegnamento,
ciclo while e costrutto condizionale, un linguaggio del
tutto equivalente a quello appena descritto. Il teorema
di Bohm Jacopini fu un argomento decisivo nel dibattito
sull'abolizione del salto incondizionato "goto", un
costrutto presente nei linguaggi di allora che rendeva
il codice estremamente difficile da capire.
Un
interpretazione superficiale del teorema di Bhom
Jacopini sembrerebbe suggerire l'inutilità di ulteriori
costrutti nei linguaggi di programmazione. Tale
affermazione equivarrebbe ad asserire l'inutilità
dell'automobile, dal momento che è possibile raggiungere
qualunque destinazione a piedi. Per quanto questo
linguaggio sia tecnicamente sufficiente per realizzare
programmi anche molto complessi, è chiaro che oltre un
certo livello, l'impresa andrebbe al di la delle
capacità umane.
L'importanza
di questo asserto risiede nel fatto che esso definisce
in modo chiaro e rigoroso un insieme di nozioni che
costituiscono, a tutti gli effetti, la base di qualunque
linguaggio di programmazione. Nei prossimi capitoli
affronteremo lo studio di costrutti più avanzati: tipi
di dati numerici, vettori, stringhe, metodi, classi ed
oggetti. Ogni nuovo strumento permetterà di realizzare
programmi più brevi e più comprensibili di quelli che si
possono scrivere con il semplice linguaggio descritto in
questo capitolo. La possibilità di creare strumenti
nuovi attraverso un uso sapiente di quelli esistenti è
senza dubbio uno degli aspetti più affascinanti della
scienza del calcolo: questo processo induttivo è la
chiave principale del rapido sviluppo
dell'informatica.
Esercizi
- Margherita Elettronica
Ogni margherita ha un numero di petali che
varia casualmente tra 30 e 50. E' risaputo che la
presenza o l'assenza di un petalo può avere delle
grosse conseguenze sulle nostre aspirazioni
sentimentali. Creare un
programma MargheritaElettronica, con una variabile
"numeroPetali" calcolata con la seguente
formula:
int
numeroCasuale = (int)(21 * Math.random()); int
numeroPetali = 30 + numeroCasuale;
Il programma, mentre conta ad uno ad uno i
petali, stampa alternativamente le scritte "m'ama" e
"non m'ama". Importante:
prima di eseguire il programma, pensare intensamente
alla persona amata.
- Shining
A voler
essere precisi, le scritte che compaiono sui fogli nel
film Shining sono state realizzate seguendo pattern
compositivi differenti dal semplice incolonnamento. Si
chiede di costruire, utilizzando in modo corretto i
cicli while, un programma che stampi su schermo una
sequenza di frasi con il seguente schema:
Il mattino ha
l'oro in bocca
  Il mattino ha l'oro in bocca
    Il mattino ha l'oro in bocca
      Il mattino ha l'oro in bocca
        Il mattino ha l'oro in bocca
          Il mattino ha l'oro in bocca
            Il mattino ha l'oro in bocca
              Il mattino ha l'oro in bocca
                Il mattino ha l'oro in bocca
                  Il mattino ha l'oro in bocca
                    Il mattino ha l'oro in bocca
  Il mattino ha l'oro in bocca
    Il mattino ha l'oro in bocca
      Il mattino ha l'oro in bocca
        Il mattino ha l'oro in bocca
          Il mattino ha l'oro in bocca
            Il mattino ha l'oro in bocca
              Il mattino ha l'oro in bocca
                Il mattino ha l'oro in bocca
                  Il mattino ha l'oro in bocca
                    Il mattino ha l'oro in bocca
Suggerimento: ricorrere a due cicli
while. Ad ogni passo del ciclo più esterno si esegua
un ciclo while di questo tipo.
numeroSpazi =
0; while(numeroSpazi < maxSpazi)
{ System.out.print("
"); numeroSpazi++; }
Ad ogni iterazione, il ciclo più esterno
incrementa la variabile 'maxSpazi': se il suo valore è
maggiore di 10, la riporta a zero.
-
Shining
2 Dopo aver
rivisto il film Shining, costruire programmi che
riproducano alcuni dei pattern presenti nel
film.
- Sistema
Operativo
Riprodurre
il sistema operativo preferito, utilizzando solo i
costrutti presenti in questo capitolo.
Invito
all'approfondimento Rapidità, precisione e capacità di eseguire in
modo instancabile operazioni ripetitive sono qualità
importantissime per un calcolatore. Se presenti in un
essere umano, tali caratteristiche tendono a conferire
una connotazione maniacale. Il cinema ricorre spesso a
questa associazione, come nel già citato Shinig o in
Rain Man, dove un Dustin Hoffmann da Oscar fornisce una
straordinaria rappresentazione delle eccezionali
capacità di calcolo di alcuni soggetti autistici,
abbinata ad una desolante incapacità di relazione
sociale.
Curiosamente
il mondo del cinema tende a rappresentare con tinte
fosche anche lo scenario inverso: per divenire davvero
pericolosa, una macchina deve prima assumere qualità
umane. La voce suadente di HAL 9000, protagonista
indiscusso del colossale 2001 Odissea nello spazio, non
serve soltanto a comunicare rotte astrali e guasti agli
impianti, ma anche a mentire, per mettere in pratica un
diabolico piano congegnato grazie alla sofisticata
euristica che lo accomuna ad un essere senziente. Matrix
e la saga di Terminator sono due esempi più recenti di
film realizzati non solo per sbancare al botteghino, ma
anche per indurre a riflettere sul desiderio di creare
macchine dotate di una quale forma di
intelligenza.
Dove si
situa, ai giorni nostri, il confine reale tra
intelligenza umana ed intelligenza artificiale? Un
computer capace di fornire una diagnosi attraverso
l'analisi dei sintomi di un paziente può essere definita
"esperto", grazie alla sua capacità di confrontare in
pochi attimi il caso in esame con milioni di altri
simili, memorizzati all'interno di un archivio
elettronico. Ma un simile "sistema esperto" non sarà mai
in grado di cogliere le sottili sfumature nella voce di
un paziente o di percepire le sue emozioni: la capacità
di cogliere questi parametri, così difficili da
quantificare e da classificare, è la qualità umana che
può distinguere un buon medico da un medico
straordinario.
Ma se mai un
giorno qualcuno dovesse riuscire a dotare una macchina
di coscienza e volontà, speriamo solo si ricordi di
plasmare la sua creazione secondo le tre leggi della
robotica di Asimov, la prima delle quali recita "Un
robot non può arrecare danno agli esseri umani, nè
permettere che degli esseri umani ricevano danno a causa
del suo mancato intervento"
Conclusioni Questo mese abbiamo consolidato le nozioni
apprese il mese scorso. Attraverso l'analisi di esempi
di complessità crescente, abbiamo cominciato a farci
un'idea di come ragiona una macchina. Il mese prossimo
approfondiremo il tema dei tipi di dati primitivi, ed
introdurremo i vettori, o array, uno dei più importanti
strumenti per la gestione della memoria.
Esempi allegati
Gli esempi completi qui descritti posson essere scaricati
tramite il seguente link
|