Logica Booleana nella Programmazione

Dal ragionamento formale al codice che decide, controlla e reagisce

Nelle lezioni precedenti abbiamo costruito un percorso completo: dalle variabili booleane agli operatori, dalle porte logiche alle espressioni, dalle leggi di Boole alla K-map. Tutto questo lavoro teorico converge qui, in una lezione dedicata esclusivamente alla programmazione pratica.

La logica booleana non è uno strato separato della programmazione che si studia e poi si dimentica: è la struttura portante di qualsiasi programma. Ogni decisione che un programma prende — mostrare o nascondere un elemento, consentire o bloccare un accesso, ripetere o interrompere un ciclo — è il risultato di una valutazione booleana. Imparare a costruire queste valutazioni in modo preciso, leggibile ed efficiente è una delle competenze fondamentali di qualsiasi sviluppatore.

Questa lezione affronta quattro aree applicative concrete: le strutture condizionali come traduzione diretta della logica booleana nel codice, i pattern di scrittura delle condizioni composte, la gestione dei casi limite legati ai valori truthy e falsy, e infine le maschere di bit come applicazione avanzata degli operatori logici su interi.

Le Strutture Condizionali: la Logica che controlla il Flusso

Una struttura condizionale è il meccanismo con cui un programma esegue blocchi di codice diversi a seconda del valore di un’espressione booleana. In PHP e JavaScript le strutture condizionali principali sono if, else if e else.

Definizione

Un’istruzione if valuta un’espressione booleana e, se il risultato è true, esegue il blocco di codice associato. Se il risultato è false, il blocco viene saltato. L’istruzione else definisce il blocco alternativo da eseguire quando la condizione è falsa.

Struttura base

<?php
$temperatura = 32;

if ($temperatura > 30) {
    echo "Fa caldo: attiva il condizionatore.";
} else {
    echo "Temperatura nella norma.";
}
// Output: Fa caldo: attiva il condizionatore.
?>
let temperatura = 32;

if (temperatura > 30) {
    console.log("Fa caldo: attiva il condizionatore.");
} else {
    console.log("Temperatura nella norma.");
}
// Output: Fa caldo: attiva il condizionatore.

La condizione $temperatura > 30 è un’espressione booleana: il confronto produce true o false, esattamente come le variabili booleane che abbiamo studiato.

Condizioni composte con AND e OR

Le condizioni reali raramente dipendono da una sola variabile. Gli operatori && e || permettono di combinare più condizioni esattamente come abbiamo fatto con le tabelle di verità.

<?php
$utente_loggato = true;
$abbonamento_attivo = true;
$account_sospeso = false;

// AND: tutte le condizioni devono essere vere
if ($utente_loggato && $abbonamento_attivo && !$account_sospeso) {
    echo "Accesso ai contenuti premium consentito.";
} else {
    echo "Accesso negato.";
}
// Output: Accesso ai contenuti premium consentito.
?>
let wifiDisponibile = false;
let datiMobiliAttivi = true;
let modalitaAereo = false;

// OR: basta che almeno una condizione sia vera
// AND: la modalità aereo deve essere disattiva
if ((wifiDisponibile || datiMobiliAttivi) &amp;&amp; !modalitaAereo) {
    console.log("Connessione disponibile.");
} else {
    console.log("Nessuna connessione.");
}
// Output: Connessione disponibile.

Catene if / else if / else

Quando le possibilità sono più di due, si usano blocchi else if in sequenza. PHP valuta le condizioni nell’ordine in cui compaiono e si ferma alla prima vera.

<?php
$punteggio = 78;

if ($punteggio >= 90) {
    echo "Ottimo";
} else if ($punteggio >= 75) {
    echo "Buono";       // questa condizione è vera: si ferma qui
} else if ($punteggio >= 60) {
    echo "Sufficiente";
} else {
    echo "Insufficiente";
}
// Output: Buono
?>

Ordine delle condizioni e corto circuito

In una catena if / else if, PHP e JavaScript smettono di valutare le condizioni non appena ne trovano una vera. Questo è un comportamento simile al corto circuito visto in A5: posizionare per prime le condizioni più probabili o più economiche da valutare migliora le prestazioni del programma.

Pattern di Scrittura delle Condizioni

Scrivere condizioni booleane corrette è necessario, ma scriverle in modo leggibile è altrettanto importante. Il codice viene letto molto più spesso di quanto venga scritto, e una condizione oscura è una fonte continua di errori e malintesi.

Pattern 1 — Assegnare la condizione a una variabile con nome significativo

Quando una condizione è complessa, assegnarla a una variabile booleana con un nome descrittivo rende il codice molto più leggibile.

<?php
// Versione difficile da leggere
if ($ruolo === "admin" && $sessione_attiva && time() - $ultimo_accesso < 3600) {
    // ...
}

// Versione leggibile con variabile intermedia
$utente_autorizzato = $ruolo === "admin"
                   && $sessione_attiva
                   && time() - $ultimo_accesso < 3600;

if ($utente_autorizzato) {
    // ...
}
?>
// Versione leggibile in JavaScript
const carrelloNonVuoto = carrello.length > 0;
const pagamentoValido = metodoPagamento !== null &amp;&amp; saldo >= totale;
const spedizioneDisponibile = regione !== "esclusa";

const ordineConfermabile = carrelloNonVuoto &amp;&amp; pagamentoValido &amp;&amp; spedizioneDisponibile;

if (ordineConfermabile) {
    confermaOrdine();
}

Pattern 2 — Early return: uscire presto dalle funzioni

Il pattern early return (uscita anticipata) consiste nel verificare le condizioni di errore o di esclusione all’inizio di una funzione e uscire immediatamente, evitando l’annidamento profondo di blocchi if.

<?php
// Versione con annidamento profondo (difficile da leggere)
function elaboraOrdine($utente, $carrello, $pagamento) {
    if ($utente !== null) {
        if (count($carrello) > 0) {
            if ($pagamento['valido']) {
                // logica principale
                return "Ordine elaborato.";
            } else {
                return "Pagamento non valido.";
            }
        } else {
            return "Carrello vuoto.";
        }
    } else {
        return "Utente non trovato.";
    }
}

// Versione con early return (molto più leggibile)
function elaboraOrdine($utente, $carrello, $pagamento) {
    if ($utente === null) {
        return "Utente non trovato.";
    }

    if (count($carrello) === 0) {
        return "Carrello vuoto.";
    }

    if (!$pagamento['valido']) {
        return "Pagamento non valido.";
    }

    // logica principale: solo se tutte le condizioni sono superate
    return "Ordine elaborato.";
}
?>
// Stesso pattern in JavaScript
function elaboraOrdine(utente, carrello, pagamento) {
    if (!utente) return "Utente non trovato.";
    if (carrello.length === 0) return "Carrello vuoto.";
    if (!pagamento.valido) return "Pagamento non valido.";

    return "Ordine elaborato.";
}

Il codice con early return corrisponde algebricamente all’applicazione sequenziale delle leggi di complementazione: ogni condizione di errore viene «cortocircuitata» prima di arrivare alla logica principale.

Pattern 3 — De Morgan applicato al codice

Come abbiamo visto in A6, le leggi di De Morgan permettono di riscrivere condizioni negate in forme equivalenti più leggibili. Scegliere la forma giusta dipende da cosa si vuole rendere esplicito al lettore.

<?php
$form_inviato = true;
$dati_validi = true;

// Forma con NOT esterno: enfatizza che stiamo gestendo un'eccezione
if (!($form_inviato && $dati_validi)) {
    echo "Impossibile procedere.";
}

// Forma con De Morgan: enfatizza le singole condizioni di errore
if (!$form_inviato || !$dati_validi) {
    echo "Impossibile procedere.";
}

// Le due forme sono equivalenti: scegliere quella più chiara nel contesto
?>

Pattern 4 — L’operatore ternario

L’operatore ternario è una forma compatta di if / else per assegnazioni semplici. La sintassi è condizione ? valore_se_vero : valore_se_falso.

<?php
$utente_loggato = true;

// Versione con if / else
if ($utente_loggato) {
    $messaggio = "Bentornato!";
} else {
    $messaggio = "Accedi per continuare.";
}

// Versione con operatore ternario: equivalente, più concisa
$messaggio = $utente_loggato ? "Bentornato!" : "Accedi per continuare.";

echo $messaggio;  // Output: Bentornato!
?>
let punteggio = 85;
let esito = punteggio >= 60 ? "Promosso" : "Rimandato";
console.log(esito);  // Output: Promosso

Quando non usare il ternario

L’operatore ternario è utile per assegnazioni semplici con una sola condizione. Diventa controproducente quando la condizione è complessa o quando i due rami contengono più operazioni. In quei casi, un if / else esplicito è sempre preferibile per la leggibilità.

Truthy, Falsy e Casi limite nel Codice reale

In A5 abbiamo visto la definizione di truthy e falsy. Qui approfondiamo i pattern pratici che emergono quotidianamente nello sviluppo web, con particolare attenzione ai casi che generano bug difficili da individuare.

Verificare se una variabile esiste e ha un valore significativo

<?php
// Scenario: verificare se un campo di un form è stato compilato

$nome = $_POST['nome'] ?? '';  // stringa vuota se non inviato

// SBAGLIATO: il controllo passa anche se $nome è "0"
// perché "0" è falsy in PHP
if ($nome) {
    echo "Nome: " . $nome;
}

// CORRETTO: controllo esplicito
if ($nome !== '') {
    echo "Nome: " . $nome;
}
?>
// Scenario: verificare se un campo è stato compilato

let nome = document.getElementById('nome').value;

// ATTENZIONE: "0" è falsy, ma potrebbe essere un valore valido
// (ad esempio, un codice postale che inizia con zero)
if (nome) {
    console.log("Nome:", nome);  // non viene eseguito se nome è "0"
}

// CORRETTO: controllo esplicito sulla lunghezza
if (nome.trim().length > 0) {
    console.log("Nome:", nome);
}

Il problema dello zero

Il valore 0 è falsy in entrambi i linguaggi, il che causa bug frequenti quando si lavora con quantità, prezzi, indici o contatori.

<?php
$quantita = 0;
$prezzo = 0.00;
$indice = 0;

// SBAGLIATO: questi controlli falliscono quando il valore è 0
if ($quantita) { echo "Quantità presente"; }   // non eseguito
if ($prezzo) { echo "Prezzo impostato"; }       // non eseguito
if ($indice) { echo "Indice valido"; }          // non eseguito

// CORRETTO: controllare esplicitamente il tipo e il valore
if ($quantita !== null) { echo "Quantità: " . $quantita; }   // eseguito
if (isset($prezzo)) { echo "Prezzo: " . $prezzo; }           // eseguito
if ($indice >= 0) { echo "Indice valido"; }                   // eseguito
?>
let punteggio = 0;

// SBAGLIATO: 0 è falsy, il messaggio non viene mai mostrato
if (punteggio) {
    console.log("Punteggio:", punteggio);  // non eseguito
}

// CORRETTO: confronto esplicito
if (punteggio !== null &amp;&amp; punteggio !== undefined) {
    console.log("Punteggio:", punteggio);  // Output: Punteggio: 0
}

Null e undefined in JavaScript

JavaScript ha due valori distinti per rappresentare l’assenza di un valore: null (assenza intenzionale) e undefined (variabile dichiarata ma non inizializzata). Entrambi sono falsy, ma non sono la stessa cosa.

let a = null;        // assenza intenzionale di un valore
let b;               // undefined: dichiarata ma non inizializzata
let c = 0;           // valore numerico valido, ma falsy

// Tutti e tre sono falsy
console.log(Boolean(a));  // false
console.log(Boolean(b));  // false
console.log(Boolean(c));  // false

// Ma non sono uguali tra loro con confronto stretto
console.log(a === b);  // false: null !== undefined
console.log(a === c);  // false: null !== 0

// L'operatore di coalescenza nulla (??) distingue null/undefined dagli altri falsy
let valore = c ?? "predefinito";
console.log(valore);  // Output: 0
// ?? restituisce il destro solo se il sinistro è null o undefined,
// non se è 0 o "" o false

let valore2 = a ?? "predefinito";
console.log(valore2);  // Output: predefinito

L’operatore di coalescenza nulla in PHP

PHP 7 ha introdotto l’operatore ?? (null coalescing), che restituisce il valore di sinistra se non è null, altrimenti quello di destra. È particolarmente utile per gestire valori opzionali provenienti da form, URL o database.

<?php
// Senza ??: verboso e ripetitivo
$pagina = isset($_GET['pagina']) ? $_GET['pagina'] : 1;

// Con ??: conciso ed espressivo
$pagina = $_GET['pagina'] ?? 1;
$ordine = $_GET['ordine'] ?? 'asc';
$filtro = $_GET['categoria'] ?? null;

// Concatenamento: se il primo è null, prova il secondo, poi il terzo
$nome = $_POST['nome'] ?? $_SESSION['nome'] ?? 'Ospite';
echo $nome;
?>

Maschere di Bit: gli Operatori Logici su Interi

Fino a questo momento abbiamo usato gli operatori logici su variabili booleane (true/false). Esiste però un secondo livello di utilizzo: applicare gli operatori logici direttamente ai bit di numeri interi. Questa tecnica si chiama manipolazione dei bit (o bitwise operations) e usa operatori dedicati.

Definizione

Gli operatori bitwise applicano l’operazione logica corrispondente a ogni singola coppia di bit degli operandi, posizione per posizione. Il risultato è un nuovo intero i cui bit sono il prodotto dell’operazione logica applicata ai bit corrispondenti dei due operandi.

OperatorePHPJavaScriptOperazione bit a bit
AND bitwise&&1 solo se entrambi i bit sono 1
OR bitwise||1 se almeno uno dei bit è 1
XOR bitwise^^1 solo se i bit sono diversi
NOT bitwise~~inverte tutti i bit
Shift sinistro<<<<sposta i bit a sinistra
Shift destro>>>>sposta i bit a destra

Per capire come funzionano, consideriamo un esempio con due numeri interi.

<?php
// 12 in binario = 1100
// 10 in binario = 1010

$a = 12;  // 1100
$b = 10;  // 1010

echo $a & $b;   // AND:  1100 AND 1010 = 1000 = 8
echo $a | $b;   // OR:   1100 OR  1010 = 1110 = 14
echo $a ^ $b;   // XOR:  1100 XOR 1010 = 0110 = 6
?>

Applicazione pratica: le Maschere di Bit per i Permessi

L’applicazione più comune e immediata delle maschere di bit è la gestione dei permessi. Invece di usare un array di variabili booleane separate ($puo_leggere, $puo_scrivere, ecc.), si usa un singolo intero in cui ogni bit rappresenta un permesso distinto. Questa tecnica è usata nei sistemi operativi Unix/Linux, nei database e in molte applicazioni web.

<?php
// Definiamo i permessi come potenze di 2 (ciascuno occupa un solo bit)
const PERMESSO_LETTURA    = 1;   // 0001 in binario
const PERMESSO_SCRITTURA  = 2;   // 0010 in binario
const PERMESSO_ESECUZIONE = 4;   // 0100 in binario
const PERMESSO_ADMIN      = 8;   // 1000 in binario

// Assegniamo permessi a un utente combinandoli con OR bitwise
$permessi_utente = PERMESSO_LETTURA | PERMESSO_SCRITTURA;
// 0001 OR 0010 = 0011 = 3
echo $permessi_utente;  // Output: 3

// Verifichiamo se un utente ha un permesso specifico con AND bitwise
if ($permessi_utente & PERMESSO_LETTURA) {
    echo "L'utente può leggere.";      // eseguito: 0011 AND 0001 = 0001 ≠ 0
}

if ($permessi_utente & PERMESSO_ESECUZIONE) {
    echo "L'utente può eseguire.";     // NON eseguito: 0011 AND 0100 = 0000 = 0
}

if ($permessi_utente & PERMESSO_ADMIN) {
    echo "L'utente è admin.";          // NON eseguito: 0011 AND 1000 = 0000 = 0
}

// Aggiungiamo un permesso con OR bitwise
$permessi_utente |= PERMESSO_ESECUZIONE;
// 0011 OR 0100 = 0111 = 7
echo $permessi_utente;  // Output: 7

// Rimuoviamo un permesso con AND bitwise e NOT bitwise
$permessi_utente &= ~PERMESSO_SCRITTURA;
// ~PERMESSO_SCRITTURA = ~0010 = ...11111101
// 0111 AND ...11111101 = 0101 = 5
echo $permessi_utente;  // Output: 5

// Invertiamo un permesso con XOR bitwise
$permessi_utente ^= PERMESSO_LETTURA;
// 0101 XOR 0001 = 0100 = 4
echo $permessi_utente;  // Output: 4
?>
// Gli stessi concetti in JavaScript
const LETTURA    = 1;   // 0001
const SCRITTURA  = 2;   // 0010
const ESECUZIONE = 4;   // 0100
const ADMIN      = 8;   // 1000

let permessi = LETTURA | SCRITTURA;  // 0011 = 3

// Verifica permesso
const puoLeggere  = Boolean(permessi &amp; LETTURA);     // true
const puoEseguire = Boolean(permessi &amp; ESECUZIONE);  // false

console.log(`Lettura: ${puoLeggere}`);     // Lettura: true
console.log(`Esecuzione: ${puoEseguire}`); // Esecuzione: false

// Aggiunta permesso
permessi |= ESECUZIONE;   // 0011 | 0100 = 0111 = 7

// Rimozione permesso
permessi &amp;= ~SCRITTURA;   // 0111 & ...11111101 = 0101 = 5

// Inversione permesso (toggle)
permessi ^= LETTURA;      // 0101 ^ 0001 = 0100 = 4

Applicazione pratica: i Colori RGB

Un altro uso comune delle maschere di bit nella programmazione web è la manipolazione dei colori. Un colore RGB a 24 bit è memorizzato come un intero in cui i bit da 16 a 23 contengono il rosso, i bit da 8 a 15 il verde, e i bit da 0 a 7 il blu.

// Un colore RGB come intero esadecimale
let colore = 0xFF8C00;  // arancione scuro: R=255, G=140, B=0

// Estraiamo i singoli canali con AND bitwise e shift destro
let rosso  = (colore >> 16) &amp; 0xFF;  // 255
let verde  = (colore >> 8)  &amp; 0xFF;  // 140
let blu    =  colore        &amp; 0xFF;  // 0

console.log(`R: ${rosso}, G: ${verde}, B: ${blu}`);
// Output: R: 255, G: 140, B: 0

// Ricostruiamo il colore dai canali con OR bitwise e shift sinistro
let nuovoColore = (rosso << 16) | (verde << 8) | blu;
console.log(nuovoColore.toString(16));  // Output: ff8c00
<?php
// Stesso concetto in PHP
$colore = 0xFF8C00;

$rosso = ($colore >> 16) & 0xFF;  // 255
$verde = ($colore >> 8)  & 0xFF;  // 140
$blu   =  $colore        & 0xFF;  // 0

echo "R: $rosso, G: $verde, B: $blu";
// Output: R: 255, G: 140, B: 0
?>

Collegamento con la teoria

Le maschere di bit sono la dimostrazione più concreta del percorso che abbiamo compiuto in questo corso. Le stesse operazioni booleane — AND, OR, XOR, NOT — che abbiamo studiato sulle variabili true/false agiscono qui sui singoli bit di un intero, esattamente come le porte logiche nei circuiti del processore. La logica booleana non è astratta: è il linguaggio con cui i computer elaborano ogni informazione, dal confronto di due parole chiave all’estrazione di un canale colore da un pixel.

Riepilogo: dalla Logica al Codice

Questo schema riassume la corrispondenza tra i concetti teorici studiati nel corso e le loro applicazioni pratiche nel codice PHP e JavaScript.

Concetto teoricoApplicazione nel codiceStrumento
Variabile booleanaVariabile true/false, flag di statobool in PHP, boolean in JS
Operatore NOTNegazione di condizione, inversione di stato!
Operatore ANDCondizioni multiple tutte necessarie&&
Operatore ORCondizioni alternative||
Operatore XORScelta esclusiva, toggle di bit^ (bitwise)
Tabella di veritàTest esaustivo di tutte le combinazioniCiclo su array di casi
Corto circuitoOttimizzazione ordine condizioni, guard clause&& e ||
Leggi di De MorganRiscrittura condizioni negate!($A && $B)!$A || !$B
AssorbimentoEliminazione condizioni ridondanti nel codiceRevisione e refactoring
AND/OR/XOR bitwiseMaschere di bit, permessi, colori RGB&, |, ^, ~

Esercizi di Verifica

Esercizio 1 — Strutture Condizionali

Scrivi il codice PHP e JavaScript per ciascuna delle seguenti specifiche. Per ogni esercizio, definisci le variabili con valori a tua scelta, scrivi la struttura condizionale e stampa il risultato.

  1. Un sistema di voto che assegna la valutazione («Ottimo», «Buono», «Sufficiente», «Insufficiente») in base al punteggio ottenuto su 100.
  2. Un controllo di accesso a una sezione premium: l’utente deve essere loggato, avere un abbonamento attivo e non avere l’account sospeso.
  3. Un sistema di sconto: applica il 20% se il totale supera 100€ e l’utente ha un coupon valido; applica il 10% se il totale supera 100€ senza coupon; altrimenti nessuno sconto.

Esercizio 2 — Early Return

Riscrivi la seguente funzione PHP usando il pattern early return per eliminare l’annidamento. Verifica che il comportamento sia identico all’originale.

<?php
function inviaEmail($destinatario, $oggetto, $corpo) {
    if ($destinatario !== '') {
        if (strpos($destinatario, '@') !== false) {
            if ($oggetto !== '') {
                if (strlen($corpo) > 10) {
                    // simula l'invio
                    return "Email inviata a $destinatario.";
                } else {
                    return "Corpo troppo breve.";
                }
            } else {
                return "Oggetto mancante.";
            }
        } else {
            return "Email non valida.";
        }
    } else {
        return "Destinatario mancante.";
    }
}
?>

Esercizio 3 — Truthy, Falsy e Casi limite

Per ciascuno dei seguenti frammenti PHP, indica quale ramo del condizionale viene eseguito e spiega perché. Poi scrivi la versione corretta che gestisce il caso limite in modo esplicito.

<?php
// Frammento 1
$risultati = [];
if ($risultati) {
    echo "Trovati " . count($risultati) . " risultati.";
} else {
    echo "Nessun risultato.";
}

// Frammento 2
$pagina = 0;
if ($pagina) {
    echo "Pagina: $pagina";
} else {
    echo "Pagina non specificata.";
}

// Frammento 3
$nome = "0";
if ($nome) {
    echo "Nome: $nome";
} else {
    echo "Nome mancante.";
}

// Frammento 4
$prezzo = 0.00;
if ($prezzo) {
    echo "Prezzo: $prezzo€";
} else {
    echo "Prezzo non impostato.";
}
?>

Esercizio 4 — Maschere di Bit

Usando il sistema di permessi visto nella lezione (LETTURA=1, SCRITTURA=2, ESECUZIONE=4, ADMIN=8), scrivi il codice PHP per ciascuna delle seguenti operazioni.

  1. Crea un utente con permessi di lettura e scrittura. Verifica con var_dump che possa leggere ma non eseguire.
  2. Aggiungi il permesso di esecuzione all’utente. Verifica che ora possa eseguire.
  3. Rimuovi il permesso di scrittura. Verifica che non possa più scrivere ma possa ancora leggere ed eseguire.
  4. Scrivi una funzione mostraPermessi($permessi) che stampa un elenco leggibile dei permessi attivi per un dato intero di permessi.

Esercizio 5 — Colori RGB

Scrivi in JavaScript una funzione rgbToHex(r, g, b) che riceve tre valori interi (ciascuno tra 0 e 255) e restituisce la stringa esadecimale del colore nel formato #RRGGBB, usando gli operatori bitwise. Poi scrivi la funzione inversa hexToRgb(hex) che riceve una stringa esadecimale e restituisce un oggetto { r, g, b }.

Esercizio 6 — Rispondi con parole tue

Rispondi alle seguenti domande scrivendo almeno tre righe per ciascuna risposta.

  1. Spiega con un esempio concreto perché il pattern early return rende il codice più leggibile rispetto all’annidamento profondo di blocchi if. Qual è la legge dell’algebra booleana che corrisponde a questo pattern?
  2. Perché il valore 0 essere falsy può causare bug difficili da individuare? Descrivi uno scenario reale in cui questo problema potrebbe manifestarsi in un’applicazione web.
  3. Le maschere di bit permettono di memorizzare più informazioni booleane in un singolo intero. Quali vantaggi offre questo approccio rispetto all’uso di un array di variabili booleane separate? In quale contesto lo useresti?
0 0 voti
Valutazione Media
Iscriviti
Notificami
guest
0 Commenti
Vecchi
Più recenti Le più votate
Feedback in linea
Visualizza tutti i commenti
Torna in alto