Visualizzazione post con etichetta python. Mostra tutti i post
Visualizzazione post con etichetta python. Mostra tutti i post

mercoledì 22 febbraio 2017

for Perl else ?

Aggiungere un "else" ad un ciclo for?
Tutto nasce dall'esigenza di eseguire del codice qualora un ciclo non venga mai eseguito...ma andiamo con ordine.
I cicli for (e foreach) di Perl si sono sempre comportati come ci si aspetta (dwim): se non ci sono iterazioni da fare non viene fatto nulla. Può sembrare stupefacente, ma Java, ad esempio, non è stato capace di fare la stessa cosa quando nella versione 5 ha inserito i "foreach":
Consideriamo una lista "objects" di oggetti di qualche tipo, ebbene il seguente blocco Java solleva una NullPointerException se la lista è nulla

for ( Object item : objects ){ ... }

mentre lo stesso ciclo Perl semplicemente non fa nulla

for my $item ( @objects ) { ... }

Ma il problema della discussione non è sulla nullità della lista, quanto sul fatto che la lista sia vuota. Nel caso la lista di oggetti sia "empty" (e/o non definita in Perl), il ciclo for non viene mai eseguito. Questo porta gli sviluppatori a scrivere codice "di guardia" per iniettare codice da eseguire qualora la lista non contenga valori:

if ( objects == null || objects.isEmpty() ){
  System.out.println( "Nessun elemento" );
}
else
  for ( Object item : objects ) { ... }

che in Perl diventa ovviamente piu' compatto:

if ( ! @objects ){ say "Nessun elemento"; }
for my $item ( @objects ) { ... }

Tralasciando l'eleganza degli operatori postfissi, l'idea Perl è quella di testare la bontà della lista: se la lista non è valida viene eseguito un blocco di codice, e comunque a seguire sempre il ciclo (che in realtà non viene mai eseguito). In sostanza in Perl non c'è l'esigenza della guardia attorno al ciclo, e questo è quello a cui mi riferivo quando intendevo dire che il ciclo for in Perl è DWIM!

Tuttavia, il dover testare la bontà della lista è una fatica doppia: viene fatta (da Perl) nel ciclo iterativo, e nel "catch" implementato dallo sviluppatore. Siccome Perl sa esattamente se la lista è buona o meno, dovendo eseguire o no il ciclo, è possibile demandare a lui questo ragionamento?
Entra nella stanza For::Else.
For::Else è un filtro sorgente (quindi che riscrive un blocco di codice) che si occupa di mettere la guardia if..else attorno al ciclo iterativo. La sintassi che ne risulta è come la seguente:

for my $item ( @objects ){ ... }
else { say "Nessun elemento"; }


ossia, come il nome suggerisce, viene fuori un for..else.
Bello?
Brutto?
Utile?
Da qui si apre un dibattito agguerrito su chi vuole una funzionalità simile e chi la ritiene inutile. In generale, diversi programmatori Perl vorrebbero un "catch" per gli iteratori, ma l'argomento è molto controverso (si veda qui e qui).

Un momento di pausa: potrebbe unless fungere allo scopo? Ovviamente no, perché sempre di una guardia attorno ad una iterazione si tratterebbe:

unless( @objects ){ say "Nessun elemento"; }
else{ for my $item ( @objects ) { ... } }


Come è noto l'uso di un unless..else è molto poco leggibile, e in generale io penso che l'unless non postfisso sia sostanzialmente inutile.

Un chiarimento per chi proviene da Python: questo linguaggio prevede una versione for..else ma il suo utilizzo appare contorto anche agli amanti dei serpenti. Anzitutto l'uso abbastanza infelice delle parole (e questo vale anche per For::Else): "else", ovvero "altrimenti", indica qualcosa da fare al posto di un'altra cosa. Nel caso del for..else di Python la clausola "else" invece indica qualcosa da eseguire quando il ciclo for termina regolarmente: il ciclo non deve eseguire del tutto o non deve essere terminato in mezzo ad una iterazione (uso di break). Uhm...

Chiarito che Python usa un for..else semanticamente differente da quello di cui si sta parlando qui, si torni alle considerazioni riguardo Perl.

Io personalmente penso non sia una buona idea implementare la clausola else in un iteratore.
Dovendo programmare con differenti linguaggi, spesso molto piu' stupidi che Perl, mi trovo comunque costretto a ricordarmi di dover inserire delle guardie, e quindi non è per me uno sforzo insormontabile "pensare" il mio codice con un test e un caso dedicato a liste "non buone".
Ma questo ovviamente vale per me.
Tuttavia considero che un simile operatore possa confondere chi si avvicina a Perl, che già risulta abbastanza complesso (chi si ricorda che for esegue un aliasing e while no? chi si ricorda che i cicli while sono pigri e i for no?), e ne uscirebbe probabilmente appesantito da un idioma poco utilizzabile.
Anzi poco usato.
Eh si, perché questo è il problema: dove si utilizzerebbe questo caso? A me personalmente viene in mente solo nella reportistica. Un caso che io affronto di frequente è quello di popolare una tabella di visualizzazione (html, o in altro formato) con tuple che provengono da un database, e in questo caso il for si comporta bene: se non ci sono tuple la tabella non viene popolata. Ma l'utente si spaventa se invece che vedere la lista dei pagamenti che deve effettuare vede uno spazio bianco, meglio quindi stampargli una stringa che dica "non ti resta nulla da pagare", e qui scatta il codice di guardia. Ma a parte questi casi, ce ne sono altri in cui si potrebbe avere una utilità pratica del costrutto for..else? A me non ne vengono in mente, e comunque anche il caso d'uso che ho citato prima riguarda piu' che altro il templating che non l'esecuzione del codice stesso, e quindi se proprio vogliamo inserire il for..else pensiamolo per il motore di template.
E se proprio si vuole utilizzare una bruttura, come forse for..else risulta, perché non usare map?

map { my $item = $_; ... } @objects or say "Nessun elemento";

In Perl 6 la situazione infatti è leggermente migliore: il ciclo for si comporta molto similmente all'operatore map, ritornando la lista degli elementi che hanno iterato correttamente, e quindi, a pena di qualche parentesi in piu', si può implementare un else mediante un or e l'idioma di map di cui sopra:

( for @objects -> $item { ... } ) || say "Nessun elemento";

Ancora una volta però è bene considerare la sanità mentale di chi dovrà mettere le mani su questo pezzo di codice, e che probabilmente proveniendo da altri linguaggi, sarà abituato al ben piu' diffuso idioma della guardia.

domenica 26 maggio 2013

L'importanza dello stile

C'è sempre stato un grosso dibattito circa lo stile di programmazione, indipendentemente dal linguaggio adottato. Lo stile raccoglie nozioni sintattiche e semantiche, ovvero come scrivere e allineare un pezzo di codice e come chiamare variabili, funzioni, e altri identificatori.
Tempo fa seguivo un thread ove mi sono permesso di esprimere un positivo commento sul linguaggio Python. Premetto che non sono un programmatore Python, ancora non riesco ad appassionarmi al linguaggio, forse perché il mio fidato compagno Perl riesce comunque a farmi svolgere gli stessi compiti. Ho comunque imparato le basi del linguaggio, proprio per sapere di cosa sto parlando quando si entra in discussione.
Ebbene un indubbio vantaggio di Python è l'indentazione basata sugli spazi. Eppure c'è ancora gente che trova questa cosa troppo rigida:
However, I've learned programming with languages where whitespace is throwaway and not having to bother about whitespace or indentation is more natural for me.

A prima vista il commento di cui sopra è piu' che ragionevole: dopotutto perché preoccuparsi di uno spazio di rroppo o di un'andata capo sbagliata? Ognuno di noi ha, inconsciamente, un proprio stile di programmazione, così come ha una propria calligrafia. E proprio come la calligrafia, anche lo stile di programmazione risulta molto influenzato dai primi insegnamenti e dagli esercizi fatti, ma non per questo risulta non-migliorabile.
Ora chiunque ritenga che un linguaggio libero nella formattazione possa essere migliore di uno rigido in tale aspetto è uno stupido (opinione ovviamente personale); analogamente lo è chi è pensa il vice-versa.
Il punto non è tanto la formattazione del codice, bensì lo stile: Python impone uno stile basato su spazi bianchi, mentre altri linguaggi non impongono tale stile. Ma di fatto per programmare in qualunque linguaggio occorre aderire ad uno stile. Non è solo una questione di sintassi, è una forma mentale.
Se si programma del codice con il proprio stile, altri programmatori potrebbero avere difficoltà nel leggere a manutenere tale codice. E se questo non rappresenta un problema per progetti "home-made", lo diventa per grossi progetti ove esistono precise regole di formattazione e di stile. La mancata applicazione di tali regole faranno si che il proprio codice sia escluso a priori, indipentemente da quanto possa essere efficace ed efficiente. L'esempio che sono solito fare è quello di un autore di libri: ogni libro deve avere una struttura che i lettori si aspettano, e quindi un indice, un titolo di capitolo, una numerazione di pagina, del testo formattato correttamente, un indice analitico, ecc. Se il libro non aderisce a tale struttura non significa che è illeggibile, ma semplicemente che la sua lettura potrebbe risultare deviata rispetto allo standard che i lettori si aspettano. Lo stesso vale per un pezzo di codice.
Il solo fatto di avere libertà di formattare il proprio codice come si vuole non significa che qualunque formattazione accettata da compilatore/preprocessore possa essere lecita. E quindi cosa succede? Si definiscono i suddetti stili. Cosa fa invece Python? Elimina in un certo senso il problema alla radice rimuovendo parte delle discussioni senza fine su dove piazzare parentesi ed andate a capo. E il solo fatto di aver rimosso tali discussioni gioca a favore del linguaggio.
Poi c'è l'aspetto creatività: molti sostengono che un linguaggio rigido nella formattazione impedisca o limiti la creatività del programmatore. Anche questo è falso: la creatività è insita nel processo, non solo nella sua implementazione. E' quindi l'algoritmo in primis che deve essere elaborato in modo creativo, non la sua implementazione. 

Se la penso così come mai non sono ancora passato a Python? Dopotutto Perl è proprio l'anti-Python dal punto di vista della sintassi e dello stile. Ebbene, come già detto, non ho ancora trovato una sfida sufficientemente avvincente da farmi passare a Python. Tuttavia questo non significa che i miei programmi Perl siano offuscati, anzi, cero sempre in ogni programma di usare una sintassi coerente e chiara, aderendo il piu' possibile allo stile del contesto in cui mi trovo.