To or || not to or?
Giorgio Sironi su Google+ un paio di giorni fa ha linkato una pagina del manuale di PHP dove si parla degli operatori logici, dove si mette in evidenza la differenza tra gli operatori booleani tradizionali && e || e gli operatori and e or. A quanto pare, per PHP, la differenza consiste nella precedenza, che è più elevata per i classici && e || rispetto a quella di and e or.
Essendomi imbattuta, durante l'utilizzo di ruby, nei medesimi operatori, mi sono chiesta se la stessa cosa valesse per questo linguaggio e la risposta è sì. Ma nella mia indagine ho scoperto che non solo anche per ruby vale il discorso della precedenza, ma a quanto pare le differenze non si limitano a questo: and e or, infatti, non vengono considerati come operatori logici, ma derivano dai medesimi operatori di Perl che sono da considerare invece come degli operatori di controlli di flusso: and può essere visto come un if e or come un unless.
Penitenza o verità?
Per chiarire il concetto, che anche a me era oscuro assai, riporto degli esempi suggeriti da Preston Lee.
Prendiamo il caso di or e pensiamo alla frase "O c'è il sole, o rimango a casa": in questo caso la disgiunzione "o" implica una conseguenza specificando un ordine di esecuzione, tant'è che potremmo parafrasare il tutto con "Se non c'è il sole, rimango a casa" (l'unless di cui sopra).
In pseudocodice, la frase diventerebbe:
$chissene = sole? or sto_a_casa($me)
o, secondo la traduzione con unless,
$chissene = sto_a_casa($me) unless sole?
È evidente in questo caso come non sia importante il valore assegnato a chissene, quello che ci interessa è che venga eseguita una delle due istruzioni e in particolare che l'ordine con cui vengano eseguite tali istruzioni sia quello indicato dall'ordine degli operandi (prima vogliamo verificare se ci sia il sole e solo dopo, eventualmente, vogliamo rimanere a casa). Quando utilizziamo ||, invece, ci interessa valutare la verità di una o dell'altra espressione su cui applichiamo l'operatore.
In virtù della differenza di precendenza, in particolare, combinare l'uso di and e or con l'operatore ternario può causare problemi perché l'operatore ternario ha precedenza più alta e potrebbe perciò non darci la risposta che desideriamo. Ad esempio, se scriviamo in console:
hash_pieno = {primo: bubu, secondo: settete}
variabile_vuota && !variabile_vuota.empty? ? variabile_vuota : hash_pieno[:secondo]
il codice produrrà, correttamente,
=> "settete"
ma se scriviamo
variabile_vuota and !variabile_vuota.empty? ? variabile_vuota : hash_pieno[:secondo]
il risultato sarà
=> nil
Alcuni suggerimenti (miei e non)
Molti tendono, per brevità, a usare degli assegnamenti nelle condizioni per gli if, cosa che personalmente ritengo essere cattiva programmazione. La mia formazione è stata sul C, con un professore inflessibile riguardo queste pratiche perciò siate clementi (Le funzioni devono avere un solo return! Non si fanno assegnazioni negli if! Le variabili devono avere nomi significativi!). Sono tuttavia consapevole che, con ruby e Rails, specialmente la prima e la terza regola cardine del Programmatore Pulito vengono bellamente ignorate, ma devo spezzare una lancia in favore di ruby e Rails (o forse devo solo calmare la mia parte condizionata che si ribella), dicendo che queste pratiche vengono solitamente sovvertite perché il codice scritto è talmente breve che non crea confusione.
Comunque, leggendo nei commenti al post di avdi, ho trovato questo impiego interessante di and e or, che sfrutta il loro essere operatori consequenziali per ovviare alla cattiva pratica dell'assegnazione nell'if e ottenere comunque l'effetto desiderato:
user = User.find_by_name('admin') and begin
# do some stuff because we found a user!
end
Per avere eventualmente una clausola else, si può pensare di fare, come suggerito nel commento più sotto, questo:
user = User.find_by_login('admin') and begin
# do some stuff, because we found a user!
true
end or begin
# do other stuff, because we didn't find a user!
end
E quindi?
Come virtuosismo, il codice appena visto è molto interessante, ma nella pratica lo eviterei per lo stesso motivo per cui sconsiglio di infrangere le tre regole cardine del Programmatore Pulito: il codice che produciamo verrà (si spera) utilizzato in un ambiente di produzione, dove è importante che il codice sia altamente manutenibile e non è detto che tutti siano in grado di interpretarlo correttamente, essendo una sintassi altamente desueta.
Come in tutti gli ambienti dove si lavora in team, inoltre, è importante che si decida per degli standard di utilizzo del linguaggio e si usino sempre quelli e difficilmente vedrete mai questo genere di cose.
Tornando al motivo del post, invece, abbiamo dunque non solo scoperto che i dinamici duo && e ||, and e or hanno precedenza diversa, ma anche che hanno funzione diversa! Proprio in virtù del voler rendere il codice leggibile e manutenibile, sarebbe perciò utile non mischiare i loro ruoli, ma soprattutto non mischiarli nel codice.