
// Funzioni

public class Esempio09 {
	static int x, a, b, r;
	
	// Una funzione si dichiara come una procedura ma
	// usando il tipo di ritorno al posto di "void"
	static int quadrato(int y) {
		return y*y; // il valore restituito
	}
	
	// Attenzione! Una funzione può contenere comandi come una procedura!
	static int quadrato2(int y) {
		System.out.println("quadrato2(" + y + "): mi avete chiamato?");
		return y*y; // il valore restituito
	}
	
	// Attenzione! Qui modifico x in modo subdolo!
	static int quadratoSubdolo(int y) {
		x++; // Argh!
		return y*y; // il valore restituito
	}
	
	// Una terribile funzione "non pura": non ritorna solo un
	// risultato ma ha un effetto collaterale su x !
	static int terribile(int y) {
		x = x + y; // Argh! Questo crea confusione
		return 2*x + y;
	}
	
	static int identità(int x) {
		System.out.println("Identità: " + x);
		return x;
	}
	
	public static void main(String[] args) {
		// Una funzione si chiama come una procedura, ma la sua chiamata
		// è un'espressione (non un comando).
		System.out.println("Il quadrato di 10 è " + quadrato(10));
		
		x = 32;
		System.out.println("Il quadrato di " + x + " è " + quadrato(x));
		
		// Questo assegnamento... causa una stampa!
		a = quadrato2(5); 

		x = 10;
		// Questo assegnamento... modifica x!
		a = quadratoSubdolo(5);
		System.out.println("a ora vale " + a); // 25
		System.out.println("x ora vale " + x); // 11 (!)
		
		// ************************
		// La funzione terribile() crea non poca confusione: facciamo un po' di esempi.
		
		// Esempio 1: "prima a, poi b"
		x = 10;
		a = terribile(1); // x=11 , a=23
		b = terribile(2); // x=13 , b=28
		r = a+b;          // r=51
		System.out.println("Il valore di r è " + r);

		// Esempio 2: "prima b, poi a"
		x = 10;
		b = terribile(2); // x=12 , b=26
		a = terribile(1); // x=13 , a=27
		r = a+b;          // r=53 : diverso da 51 (!)
		System.out.println("Il valore di r è " + r);

		// Esempio 3: "hic sunt leones"
		x = 10;
		r = terribile(1) + terribile(2); // Quanto vale???
		System.out.println("Il valore di r è " + r); 

		// Attenzione: il programma sopra è inerentemente ambiguo.
		// Il suo risultato dipende se prima viene valutata terribile(1)
		// o terribile(2). Molti linguaggi di programmazione *non* specificano in
		// che ordine questo deve avvenire. È possibile quindi
		// che il programma funzioni in un modo o in un altro in modo impredicibile.
		//
		// Questo può cambiare per esempio da una versione del linguaggio all'altra.
		// O anche in teoria da un computer all'altro, o alla fase lunare, o ...
		//
		// Nel caso particolare di Java (ma non per esempio in C o C++), è
		// garantita una valutazione da sinistra a destra. Ciò nonostante,
		// è preferibile non fare *mai* affidamento su un particolare ordine di valutazione.
		//
		// Dal punto di vista teorico, la semantica delle espressioni con "effetti collaterali"
		// non è più della forma
		//     P( Exp * Store * Value )		dove Value = Z (interi) + String + ...
		//     <e , sigma> -->e  v
		// ma è della forma
		//     P( Exp * Store * Value * Store )
		//     <e , sigma> -->e  <v , sigma'>
		// cioè, la valutazione delle espressioni può modificare lo store, così
		// come l'esecuzione di un comando!
		
		
		// Il fatto di sopra ci aiuta anche a distinguere la
		// precedenza degli operatori dall'ordine di valutazione.
		// Per esempio:
		x = 10;
		r = terribile(1) * terribile(2) + terribile(3);
		// La precedenza degli operatori ci dice che il * viene eseguito
		// prima del +, cioè che il comando di sopra si legge come
		x = 10;
		r = ( terribile(1) * terribile(2) ) + terribile(3);
		// Questo però NON vuole dire che terribile(1) e terribile(2) vengano
		// eseguiti prima di terribile(3) ! Le tre chiamate potrebbero essere
		// eseguite in *qualunque* ordine, fermo restando che i loro risultati
		// vanno prima moltiplicati e poi sommati come impongono le parentesi sopra.
		//
		// Riassunto: se nella stessa espressione chiamate più funzioni, non
		// potete assumere un ordine di valutazione. Se vi serve, usate l'assegnamento
		// per spezzare l'espressione in più comandi.
		
		
		// C'è un'importante eccezione al discorso fatto sopra.
		// Gli operatori booleani AND (&&) e OR (||) valutano sempre
		// le espressioni da sinistra a destra. Questo vale in quasi tutti
		// i principali linguaggi imperativi (Java,C#,C,C++,...).
		if ((identità(5) == 5) && (identità(6)==6)) {
			System.out.println("Prima di questa stampa ci sono 5 e 6, nell'ordine");
		}
		// Inoltre, nel caso di AND e OR la seconda espressione NON viene
		// valutata a meno che "non serva". Per esempio:
		if ((identità(5) == 5) || (identità(6)==6)) {
			System.out.println("Prima di questa stampa c'è 5 ma NON 6");
		}
		// Si noti che sopra la funzione identità viene chiamata solo una
		// volta, con argomento 5. Dopo che il primo test di uguaglianza
		// viene valutato come vero, l'OR non "ha più bisogno" di valutare
		// la seconda parte, ma viene subito valutato come vero.
		// Questo fa sì che gli effetti collaterali della valutazione
		// della seconda parte NON abbiano luogo! Sopra, la stampa di 6
		// non viene fatta.
		
		// Nel caso dell'AND, se la prima espressione valuta come falso,
		// la seconda espressione non viene valutata.
		
		// Esercizio (avanzato): provate a scrivere le regole della
		// semantica delle espressioni per questo tipo di valutazione.
		
		// Questo tipo di valutazione viene chiamata valutazione in "corto-circuito"
		// o a volte valutazione "lazy". Un suo utilizzo tipico è il seguente:
		
		x=0;
		if (x==0 || 100/x > 5 ) { // 0==0 || 100/0 > 5
			System.out.println("Questo viene stampato, non ci sono errori di divisione per zero.");
		}
	
		// Esercizio: dimostrare con un esempio che l'operatore OR non è commutativo (!!).
		
	}

}
