venerdì 28 ottobre 2016

not $foo++

Sono inceppato nell'impementazione di List::MoreUtils::PP::uniq e ho notato una cosa che mi ha fatto sorgere un grosso punto di domanda: un uso quasi malsano dell'operatore not. Ma in fondo è questo il bello di Perl: riuscire ad esprimere concetti molto difficili con pochissimo codice. Ecco quindi il codice in esame:


sub uniq (@)
{
    my %seen = ();
    my $k;
    my $seen_undef;
    grep { defined $_ ? not $seen{ $k = $_ }++ : not $seen_undef++ } @_;
}
 
Il trucco che fa funzionare il tutto è in quel pezzo not $seen{ $k = $_ }++, e in particolare nella negazione. Provo a smontare il codice far comprendere come io sono giunto alla soluzione. Anzitutto la funzione grep processa la lista di paramentri e si aspetta un risultato true o false a seconda del fatto che lo si voglia restituire in uscita dalla funzione (essendo grep l'ultima istruzione effettuata si ha un return implicito). Se il parametro corrente $_ è valido (defined) lo si usa come chiave di un hash %seen e si incrementa (con autovivification) il valore della cella relativa. Fin qui tutto facile, ma occorre ricordarsi che l'operatore postfisso ++ restituisce il valore prima del suo incremento. In sostanza, la prima volta che si trova un elemento nella lista la relativa cella di %seen viene incrementata ma il valore restituito dall'operazione di ++ è zero (ossia falso). Questo valore viene passato all'operatore not che a sua volta lo riconverte in contesto boolean a true e l'elemento viene "greppato" nella lista di ritorno. Alla successiva occorrenza dello stesso elemento la sua cella in %seen viene incrementata, l'operatore ++ restituisce il vecchio valore (1) e questo viene forzato in contesto boolean (quindi true) e negato portando quindi il valore a false, scartando quindi l'elemento. Cosa analoga avviene per i valori undefined, che però in questo caso vengono considerati tutti uguali e quindi non si ha bisogno di tenerli come chiave di un hash.
Direi un bell'esempio di come Perl 5 possa portare a codice molto compatto che però richiede un po' di potenza mentale per essere interpretato!

Nessun commento: