sabato 1 marzo 2014

Autovivification e defined-or in Perl

La regola che guida Perl e' quella di poter fare quello che si vuole con il minimo sforzo.
A tal fine Perl offre il concetto di "autovivification", noto anche a molti altri linguaggi di scripting, che consente di inizializzare una variabile (scalare o non) al suo primo accesso. L'idea e' quella di non dover pre-inizializzare le strutture dati, ma lasciare che questi si popolino in automatico quando occorre.
Come conseguenza dell'autovivificazione l'accesso ad un entry in un hash che non e' ancora stat definita produce l'inserimento dell'netry con valore undef, cosi' come l'accesso ad una cella di un array con posizione non ancora conteggiata implica la sua inizializzazione (e di tutte le celle precedenti) al valore di zero.
Perche' questo dovrebbe semplificare le cose? Perche' invece che scrivere un blocco di codice come segue:

%my_hash = ( FILES => 0,
DIRS => 0,
LINKS => 0 );
# ...
$my_hash{ FILES }++;
# ...
$my_hash{ DIRS }--;
# ...

e' possibile scrivere direttamente:

%my_hash;
# ...
$my_hash{ FILES }++;
# ...
$my_hash{ DIRS }--;
# ...

Perl e' sufficientemente intelligente da capire anche i riferimenti, e quindi tutto funziona correttamente anche usando strutture dati complesse.
Anche se esistono opportuni moduli per ridurre o eliminare l'autovivificazione (ad esempio evitando di popolare celle con chiavi mispelled), l'autovivification e' una proprieta' veramente molto utile.

Ma Perl non si ferma qui: le versioni recenti definiscono anche l'operatore "defined-or". Tale operatore, dalla sintassi a mio avviso orribile, permette di inizializzare in autovivificazione un elemento solo se questo non e' ancora stato definito.
La sintassi e' per me orribile perche' l'operatore di assegnamento "defined-or" usa, ovviamente, il simbolo '=' (assegnamento) preceduto da una doppia slash '//' (ad indicare e ricordare il simbolo di or logico '||'); tale sintassi ricorda vagamente un commento C++/Java e la cosa e' a prima vista fuorviante nella lettura del codice.
Ad ogni modo come funziona l'operatore? Si consideri il seguente esempio:

#!/usr/bin/env perl -w

use v5.10;
use Data::Dumper;

sub initialize{
  my ($hash_ref) = @_;

  $hash_ref->{ name } //= "Luca";
  $hash_ref->{ surname } //= "Ferrari";
}


$my_hash_ref = { name => "Mr. Luca" };
say Dumper( $my_hash_ref );
initialize( $my_hash_ref );
say "After the defined-or operator....";
say Dumper( $my_hash_ref );

che produce come output:

$VAR1 = {
  'name' => 'Mr. Luca'
};

After the defined-or operator....
$VAR1 = {
  'name' => 'Mr. Luca',
  'surname' => 'Ferrari'
};


L'idea e' questa: viene definito un hash che deve contenere come chiavi 'name' e 'surname', ma per dimenticanza (o pigrizia) solo il primo viene valorizzato. Poi si chiede alla funzione 'initialize' di inizializzare l'hash e questa esamina le due chiavi. La prima, 'name', e' definita e quindi non viene riassegnata, mentre la seconda non e' ancora stata definita e quindi viene assegnata ad un valore di default.
Valore di default: questo e' l'uso principale dell'operatore "defined-or".
In altre parole e' come se si fosse scritto:

if ( ! exists $hash_ref->{ surname } ){
   $hash_ref->{ surname } = "Ferrari";
}

Nessun commento: