Salta al contenuto
Note per Studenti Esercizio 3 · condizioni, cortocircuito e divisioni

Esercizio 3condizioni, cortocircuito e divisioni

Esame
In questa pagina 8

Testo (questionario di dicembre 2022 e test d'esame di dicembre 2012 e gennaio 2014 del corso di Fondamenti di Informatica UniPD; adattato da un tema d'esame in Java). Teoria: Logica booleana e istruzione ifValori di verità, operatori di confronto e logici con valutazione a corto circuito, leggi di De Morgan, truthiness; if/elif/else, espressione condizionale e match.Logica booleana e istruzione if →, Tipi numerici ed espressioniint, float, complex e bool; operatori aritmetici con divisione intera e modulo; precedenza; conversioni di tipo; modulo math e arrotondamenti.Tipi numerici ed espressioni →, Eccezioni in PythonEccezioni e traceback, eccezioni predefinite più comuni, try/except/else/finally, raise per segnalare errori, propagazione lungo le chiamate.Eccezioni in Python →.

  1. Il metodo if ((a % b == 0) && (b != 0)) return true; else return false; può lanciare un'eccezione? E se si scrive if ((b != 0) && (a % b == 0))? Come si traduce in Python?
  2. In Java public void prova(int x) { if (-(x / 2) != -x / 2) throw new IllegalArgumentException(); } lancia l'eccezione per qualche xx intero? In quali circostanze? E in Python?
  3. Con P1P_1 e P2P_2 veri e P3P_3 falso, valutare P1 or P2 and P3, (P1 or P2) and P3 e not P1 or P2 and not P3.
  4. Sapendo a = False, che valore ha (a and b) or (not a)? E con a = True?
  5. Che cosa restituisce alfa = "ciao"; return 1 if alfa == "ciao mare" else 2 if alfa.startswith("ciao") else 3? E che differenza c'è tra == e is per le stringhe?
  6. Il metodo public static double valuta(double a, double b) { return a / b; } (Java) può lanciare ArithmeticException se b vale 0? Che cosa fa in Python e in C?

1. Cortocircuito e divisione per zero

Gli operatori and e or valutano da sinistra a destra e si fermano appena il risultato è noto (cortocircuito): False and ... è sicuramente falso, True or ... è sicuramente vero, il secondo operando non viene calcolato.

  • (b != 0) and (a % b == 0): con b=0b = 0 il primo operando è falso e a % b non viene mai eseguito → nessuna eccezione.
  • (a % b == 0) and (b != 0): a % b viene calcolato per primo; con b=0b = 0 solleva ZeroDivisionError (in Java ArithmeticException).

L'ordine degli operandi non è una questione di stile: la condizione "di guardia" va prima.

2. Divisione intera sui negativi

In Java e in C, / tra interi tronca verso zero: 7/2=37/2 = 3 e (−7)/2=−3(-7)/2 = -3. Allora −(x/2)-(x/2) e (−x)/2(-x)/2 coincidono sempre, tranne nell'overflow: per x=−231x = -2^{31} il valore −x=231-x = 2^{31} non sta in 32 bit e "ricomincia" da −231-2^{31}, quindi (−x)/2=−230(-x)/2 = -2^{30} mentre −(x/2)=+230-(x/2) = +2^{30}. L'eccezione è lanciata solo per x=−231x = -2^{31}.

In Python l'operatore // fa invece il quoziente per difetto (verso −∞-\infty): −7//2=−4-7 // 2 = -4. Il confronto diventa vero per ogni xx dispari, e non c'è overflow perché gli interi sono illimitati. Il resto cambia di conseguenza: -7 % 3 vale 22 in Python (−1-1 in Java e C).

3. Precedenze

Da più alta a più bassa: not, poi and, poi or (come moltiplicazione e somma). Con P1=P2=VP_1 = P_2 = V, P3=FP_3 = F:

  • P1 or P2 and P3 = P1 or (P2 and P3) = V∨(V∧F)=V∨F=V \lor (V \land F) = V \lor F = True;
  • (P1 or P2) and P3 = V∧F=V \land F = False;
  • not P1 or P2 and not P3 = (not P1) or (P2 and (not P3)) = F∨(V∧V)=F \lor (V \land V) = True.

4. Semplificare con la tabella di verità

Quando le variabili sono poche si prova ogni combinazione:

a b (a and b) or (not a)
F F V
F V V
V F F
V V V

Con a = False è sempre vero; con a = True vale b (la colonna coincide con b). L'espressione è l'implicazione a⇒ba \Rightarrow b.

5. Stringhe: == e is

alfa == "ciao mare" confronta i valori: "ciao" ≠ "ciao mare", falso; alfa.startswith("ciao") è vero: restituisce 22. (In Java l'operatore == su due String confronta i riferimenti, e per il testo si deve usare equals: l'errore tipico del quiz originale. In Python == è già il confronto per valore.)

is verifica l'identità: lo stesso oggetto in memoria. Due stringhe uguali costruite in modo diverso possono essere oggetti distinti: "".join(["ci", "ao"]) is "ciao" è di solito False, mentre == dà True. Per le stringhe e i numeri non si usa is (il risultato dipende dall'interprete); si usa is solo con None e con True/False (Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →).

6. Divisione per zero tra numeri in virgola mobile

In Java (e in C) la divisione tra double segue lo standard IEEE 754 e non lancia eccezioni: a/0,0a/0{,}0 vale +∞+\infty se a>0a > 0, −∞-\infty se a<0a < 0, NaN (not a number) se a=0a = 0; l'ArithmeticException esiste solo per la divisione tra interi. In C 1.0 / 0.0 dà inf. In Python invece anche la divisione tra float solleva ZeroDivisionError: 1 / 0.0 è un errore, mentre float("inf") e math.inf esistono come valori. Si ricorda anche che NaN non è uguale a niente, nemmeno a se stesso: float("nan") == float("nan") è False, si usa math.isnan.

Verifica in Python

python
def valuta_ok(a, b):
    return b != 0 and a % b == 0       # la guardia prima

def valuta_ko(a, b):
    return a % b == 0 and b != 0       # a % b prima della guardia

print(valuta_ok(6, 0), valuta_ok(6, 3))        # False True
try:
    valuta_ko(6, 0)
except ZeroDivisionError as e:
    print("ZeroDivisionError:", e)

def wrap32(n):                                  # un intero a 32 bit "alla Java"
    n &= 0xFFFFFFFF
    return n - (1 << 32) if n >> 31 else n

def div_java(a, b):                             # divisione che tronca verso zero
    q = abs(a) // abs(b)
    return q if (a < 0) == (b < 0) else -q

casi = list(range(-1000, 1001)) + [2 ** 31 - 1, -2 ** 31]
print([x for x in casi if wrap32(-div_java(x, 2)) != div_java(wrap32(-x), 2)])   # [-2147483648]
print([x for x in range(-6, 7) if -(x // 2) != (-x) // 2])                       # [-5, -3, -1, 1, 3, 5]
print(-7 % 3, -7 // 3, div_java(-7, 3))                                          # 2 -3 -2

P1, P2, P3 = True, True, False
print(P1 or P2 and P3, (P1 or P2) and P3, not P1 or P2 and not P3)              # True False True
for a in (False, True):
    for b in (False, True):
        print(a, b, (a and b) or (not a))

alfa = "ciao"
print(1 if alfa == "ciao mare" else 2 if alfa.startswith("ciao") else 3)        # 2

import math
try:
    1 / 0.0
except ZeroDivisionError as e:
    print("ZeroDivisionError:", e)                  # float division by zero
print(math.inf, float("nan") == float("nan"), math.isnan(math.inf - math.inf))   # inf False True
print("ciao" == "".join(["ci", "ao"]))                                         # True

Errori comuni

  • Mettere la condizione che può fallire prima della guardia.
  • Credere che / e // coincidano per i negativi, o che % abbia lo stesso segno in tutti i linguaggi.
  • Confondere le precedenze di and e or e non parentesizzare.
  • Usare is per confrontare stringhe o numeri.
  • Credere che la divisione per zero tra float produca lo stesso risultato in tutti i linguaggi (Python: errore; Java e C: inf o NaN).

Versione ripasso

Questionario di dicembre 2022 e test di dicembre 2012 e gennaio 2014 (UniPD, in Java; adattato a Python). Teoria: Logica booleana e istruzione ifValori di verità, operatori di confronto e logici con valutazione a corto circuito, leggi di De Morgan, truthiness; if/elif/else, espressione condizionale e match.Logica booleana e istruzione if →, Tipi numerici ed espressioniint, float, complex e bool; operatori aritmetici con divisione intera e modulo; precedenza; conversioni di tipo; modulo math e arrotondamenti.Tipi numerici ed espressioni →.

  • Cortocircuito: and e or valutano da sinistra e si fermano appena il risultato è noto. b != 0 and a % b == 0 non dà mai errore; a % b == 0 and b != 0 solleva ZeroDivisionError per b=0b = 0. La guardia va prima.
  • Divisione intera: in Java/C / tronca verso zero, in Python // va per difetto (−7//2=−4-7 // 2 = -4, -7 % 3 =2= 2). −(x/2)≠(−x)/2-(x/2) \ne (-x)/2 in Java solo per x=−231x = -2^{31} (overflow); in Python per ogni xx dispari.
  • Precedenze: not > and > or. Con P1=P2=VP_1 = P_2 = V, P3=FP_3 = F: P1 or P2 and P3 = True; (P1 or P2) and P3 = False; not P1 or P2 and not P3 = True.
  • Tabella di verità per semplificare: (a and b) or (not a) è a⇒ba \Rightarrow b: sempre vero se a è falso, uguale a b se a è vero.
  • == confronta i valori, is l'identità: is solo con None, True, False (Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →). In Java == su String confronta i riferimenti (serve equals).
  • Divisione tra float per 00: in Java e C vale ±∞\pm\infty o NaN (IEEE 754), in Python solleva ZeroDivisionError; NaN è diverso anche da se stesso.

Errori comuni: guardia dopo l'operazione che può fallire; / e // scambiati; precedenze senza parentesi; is su stringhe e numeri.

Teoria collegata