-+  Documenti
 |-  Bibliografia
 |-  Articoli
 |-  Perlfunc
 |-  F.A.Q.
 |-  F.A.Q. iclp
-+  Eventi
-+  Contatti
-+  Blog
-+  Link



 

Versione stampabile.


Aldo Calpini
Filosofo fallito, programmatore per vocazione. Nato e cresciuto a Roma, ha deciso di andare per il mondo in cerca di fortuna. Non l'ha trovata, ma ora vive e lavora a Vienna e non si lamenta troppo spesso.

Capita ogni tanto che, sul canale IRC #perl.it, qualcuno o qualcosa venga ad accendere una scintilla d'interesse in una altrimenti grigia e noiosa giornata lavorativa. Cos'è un canale IRC, chiedete? Quello che comunemente passa sotto il nome di chat, e nella fattispecie la chat sulla quale si possono trovare molti degli autori di Perl.it (praticamente tutti). Se qualcuno di voi sta pensando che la chat sia una cosa da scellerati perdigiorno o adolescentelli brufolosi che non hanno una vera vita, si dovrà ora ricredere. Perlomeno per quanto riguarda i brufoli, che personalmente avevo una quindicina d'anni fa :-)

La scintilla, dicevamo, si è accesa qualche giorno fa (era giovedì 23 novembre 2006), quando gaspa ci ha "sfidato" parlando d'introspezione. Più che un articolo, di fatto questo è un racconto: direttamente dalla "viva voce" di #perl.it (i commenti, ovviamente, sono miei e quindi di parte :-). Premetto che l'argomento "tecnico" potrebbe risultare ostico a chi si è avvicinato da poco al Perl. Chi, al contrario, ama cimentarsi in questioni di lana caprina e intricati sofismi, è ovviamente invitato a proseguire la discussione utilizzando i commenti. Buona lettura...

Presentiamo innanzitutto rapidamente i personaggi di questa storia: dada, che sono io; dree e dakkar, due delle "colonne" di #perl.it; e infine gaspa, giovane e promettente perlista. La scena inizia, dunque, proprio con l'ingresso in canale di gaspa.

[17:49] gaspa has joined #perl.it
[17:49] <boha> anvedi come balla gaspa
[17:49] <dree> ceo gaspa !
[17:49] <gaspa> ciao boha
[17:49] <gaspa> domanda sintattice
[17:49] <gaspa> sintattica
[17:49] <gaspa> (forse anche sintetica)
[17:49] <gaspa> se %main:: e` un'hash
[17:50] <gaspa> come faccio a prendere uno dei suoi valori... cioe' se $name e`
        una chiave
[17:50] <gaspa> faccio %main::$name ???

Ahi ahi. Ha tirato fuori %main::, già sento odore di bruciato... Può sembrare una sintassi un po' strana, a prima vista, e anche alla seconda in effetti. La hash %main::, con i doppi due punti finali, rappresenta la symbol table del package main. Questo vale anche per qualunque altro package, ad esempio %Foo:: contiene la symbol table del package Foo. Il package main, per chi non lo sapesse, è il package principale di un programma Perl (ossia, quello che stiamo usando quando non usiamo nessun package).

Ma cos'è precisamente una symbol table? Come dice il nome, è la tabella di tutti i simboli definiti in un determinato package. Provate questo semplice script sul vostro Perl:

        use Data::Dumper;
        print Dumper \%main::;

Uscirà fuori, probabilmente, un sacco di robaccia. Ma se guardate più attentamente, noterete che alcuna di quella "robaccia" sembra avere un senso... Ad esempio, ci troviamo chiavi tipo ARGV, INC, ENV: sono le variabili predefinite @ARGV, @INC e %ENV. Fate ora un'altra prova:

        use Data::Dumper;
        $x = 42;
        print Dumper \%main::;

Se confrontate l'output con quello dello script precedente, vedrete una riga in più:

        'x' => *::x,

Cosa vuol dire? Che stavolta nel package main è stato definito un simbolo in più, "x". Che sarebbe poi la nostra variabile $x. Da notare che se avessi scritto my $x = 42, non l'avrei trovato nella symbol table: perché è una variabile lessicale, non "globale" del package, e si trova in un pad, non nella symbol table... ma stiamo divagando.

Torniamo al canale, dove gaspa aveva buttato là un improbabile %main::$name. La risposta è secca e asciutta.

[17:50] <dada> no
[17:50] <gaspa> direi di no...
[17:50] <gaspa> infatti:-P

Ma gaspa non demorde, e rilancia.

[17:53] <gaspa> (cioe', mi spiego, vorrei ispezionare lo spazio di nomi di un p
        ackage...)

Ecco il punto. Quello che si chiama introspezione, nel gergo del programmatore, indica la possibilità di un linguaggio di ispezionare, appunto, le proprie strutture dati; ossia, le variabili definite, le funzioni, classi, metodi, etc. In sostanza, il programma stesso che il linguaggio sta in quel momento eseguendo. Nobile intento davvero. Ma rimane il problema di come accedere alle chiavi di %main::.

[17:54] <dree> $main::{$name} ?
[17:54] <dada> nemmeno
[17:54] <dada> direi che, sintatticamente parlando, non si puo' fare

Col senno di poi, bisogna dire che dree aveva (quasi) ragione. Ma lì per lì non sono stato molto a ragionarci e, come mio solito, ostentando sicumera le ho sparate grosse :-)

[17:55] <gaspa> beh, cosi' mi esce qualcosa:
[17:55] <dada> in teoria dovrebbe essere ${'main::'}{$name}
[17:55] <gaspa> %main->{$name}
[17:56] <dada> gaspa: non credo abbia molto senso
[17:56] <gaspa> invece da 'lo stesso risultato del tuo...
[17:56] <gaspa> (non mi chiedere il motivo... :-P sicuramente potresti spiegarl
        o tu a me... :-D )

La buona volontà di gaspa è encomiabile, ma la sintassi %main->{$name} è decisamente sbagliata. In quel momento però vengo "distratto" per motivi di lavoro, e per 5 minuti gaspa rimane convinto della sua posizione.

[17:57] <gaspa> si', dada, confermo che il risultato e' lo stesso...
[17:57] <gaspa> pero' non mi riesco a spiegare molto...
[17:59] <gaspa> ah, no... non e' vero..

Finché non si rende conto da solo di non essere sulla buona strada. Chissà che cosa stava provando a fare... durante la mia assenza, un'altra "stoccata" di dree.

[18:00] <gaspa> c'e' un qualche posto/libro/chenneso che spieghi un po' di intr
        ospezione in perl?
[18:00] <dree> i moduli Devel:: ?

Ad essere sincero, mi sono accorto di questo suggerimento di dree solo riguardando i log. Sta di fatto che esiste un modulo, chiamato Devel::SymDump, che non abbiamo menzionato ma che fa esattamente il lavoro che gaspa stava cercando, in qualche modo, di riprodurre. Un punto per dree++.

Io, nel frattempo, mi ero messo a cercare uno script che ricordavo di aver scritto svariati anni prima, per la precisione questo. Nella sub DumpNames (che a mia volta avevo "mutuato" da uno script della ActiveState, ma sinceramente non ricordo più quale) utilizzavo appunto la sintassi %Package:: per accedere alla symbol table. Una rapida scorsa al codice e sento di avere ormai l'argomento in pugno.

[18:00] <dada> allora
[18:00] <dada> a quanto mi risulta
[18:00] <dada> le chiavi di %main:: sono "glob"
[18:01] <dada> per cui, per accedere ai vari slot delle glob
[18:01] <dada> devi fare una cosa tipo:
[18:01] <dada> ${"main::$name"} # slot scalare
[18:01] <dada> %{"main::$name"} # slot hash
[18:01] <gaspa> ok
[18:01] <dada> @{"main::$name"} # slot array
[18:01] <dada> &{"main::$name"} # slot funzione
[18:02] <dada> volevo dire "typeglob", non "glob"
[18:02] <gaspa> mmm... ok...

Ho tirato in ballo un sacco di concetti (col segreto intento, ovviamente, di seminare il panico :-). Un typeglob è una bestia strana, in Perl. Ed è appunto quello che viene puntato, nella symbol table, dal nome che gli corrisponde.

Se riprendete l'esempio precedente, quando abbiamo scritto $x = 42, avrete notato che, associato al nome "x", la symbol table conteneva *::x, ossia (semplificando) *x. Che è proprio la sintassi Perl per esprimere un typeglob.

In due parole, un typeglob rappresenta tutte le variabili, a prescindere dal "sigillo", di un certo nome (il sigillo sarebbe il $, @ o % davanti al nome di una variabile). Le variabili vere e proprie (o meglio: reference alle variabili) si trovano nei vari slot del typeglob, ai quali si accede come fossero chiavi di hash. Esempio:

        *x         # typeglob
        *x{SCALAR} # slot scalare (ossia $x)
        *x{ARRAY}  # slot array (ossia @x)
        *x{HASH}   # slot hash (ossia %x)
        *x{CODE}   # slot codice (ossia sub x)
        *x{IO}     # slot filehandle (ossia open x, ...)

E a cosa serve una cosa del genere, chiedete? Beh, storicamente i typeglob sono gli antenati delle reference (c'erano già ai tempi del Perl 4); inoltre, fino a qualche versione di Perl fa, erano l'unico modo per passare un filehandle come argomento ad una subroutine, quindi potreste aver visto codice del tipo:

        open(FILE, ">output");
        print_report(*FILE);

Oggi, però, si preferisce a quest'idioma il più moderno:

        open(my $file, ">output");
        print_report($file);

Infine, come abbiamo già detto, i typeglob sono appunto quello che viene "puntato" dalle chiavi presenti nella symbol table.

Intanto, sul canale, non sapendo ancora cosa m'aspetta proseguo spavaldo nella mia esposizione.

[18:02] <dada> non c'e' modo, che io sappia, di sapere "a priori" se un typeglo
        b e' uno scalare, un hash, un array, etc.
[18:02] <dakkar> dada: non mi pare...

Ora, per chi non lo conoscesse, dakkar è il castigamatti di #perl.it. Non importa di cosa si stia parlando, se Perl o teoria degli automi a stati finiti o biologia macromolecolare o omelette al prosciutto, se qualcuno in canale si lascia sfuggire anche una lieve imprecisione, dakkar arriva con puntualità svizzera e spiega, con dovizia di particolari, dove e perché stai sbagliando. dakkar, per definizione, sa. Mi metto, quindi, tranquillamente in attesa della bastonata...

[18:03] <dakkar> *main::{$name}=\42 assegna allo scalare
[18:03] <dakkar> *main::{$name}=\@gino assegna all'array
[18:03] <dada> dakkar: ah
[18:03] <dakkar> non c'è modo di saperlo perché un typeglob punta a *tutti* i t
        ipi

Che è poi fondamentalmente quello che tentavo di dire io. Il fatto di utilizzare il typeglob in assegnazione non mi era in effetti venuto in mente, ma si stava parlando di utilizzarlo in lettura... Un po' titubante, cerco di vedere se ho capito bene.

[18:04] <dada> dakkar: quindi *main::{$name} dovrebbe puntare a $name nello sta
        sh %main:: ?
[18:05] <dakkar> no
[18:05] <dada> gia'

Heh. Ovviamente non avevo capito come mi sembrava. Per fortuna, non sono il solo :-)

[18:05] <gaspa> ... comincio a fare confusione...
[18:05] <dakkar> se $name='gino', *main::{$name} è come *gino
[18:05] <dakkar> al che ho dei dubbi sulla sintassi di accesso a main::
[18:05] <dakkar> gaspa: anch'io
[18:06] <dada> hehee
[18:06] <gaspa> :-D

La cosa si fa divertente: sembrava tutto chiaro, e ora siamo in 3 ad avere dubbi. Ci deve essere qualcosa che ci sfugge... Da parte mia, tento di chiuderla lì con una boutade.

[18:07] <dada> gaspa: conviene fare come ti ho scritto io prima e accettare la
        dura realta'
[18:07] <dada> da qualche parte deve pur esistere una sintassi d'accesso divers
        a, ma del resto anche gli angeli devono pur avere un sesso

gaspa sembra quasi convinto. Ma c'è quel quasi che non gli torna, e mi spinge ad espormi con qualche riga di codice dal succitato programmino... cosa che ovviamente suscita le ire del dakkar!

[18:08] <gaspa> mah, facendo come mi hai detto trovo qualcosa
[18:08] <gaspa> ma non posso conoscerne il tipo... ref() non torna nulla, effet
        tivamente
[18:08] <dada> io facevo cosi'
[18:09] <dada> if(defined ${"$package::$symname"}) { print "is a scalar\n"; }
[18:10] <dakkar> grazialcaz
[18:10] <dada> if(defined &{"$package::$symname"}) { print "is a sub\n"; }
[18:10] <dada> etc
[18:10] <gaspa> lol
[18:10] <dakkar> con rispetto parlando ;-)

Eh si, dakkar ha ragione. Non sto usando il typeglob, né tantomeno sto accedendo alla symbol table del package. Sto componendo il nome tramite una stringa, come si fa ad esempio in:

        $pippo = "ciao";
        $pluto = "pippo";
        print ${$pluto}; # stampa "ciao"

Tale pratica, solitamente, viene punita seduta stante col bacchettamento delle dita. Innanzitutto non funziona con le variabili my, e questo già di per sé la dice lunga. In secondo luogo, ha la triste fama di essere utilizzata per effettuare il dispatch di funzioni in base a parametri CGI, cosa che ne fa un potenziale buco di sicurezza grande quanto il traforo del Frejus. Ma mentre noi siamo in gravi ambasce, dakkar si sta dando da fare.

[18:10] <dakkar> cmq mi pare sia in perlmod
[18:10] <dakkar> confermo
[18:11] <dakkar> $main::{foo} è come *main::foo ovvero (spesso) *foo
[18:11] <dakkar> e dopo questa, fuggo
[18:11] dakkar has quit IRC ("e via, verso nuove avventure")

Hah! L'infame, ci lascia così. Beh, del resto era piuttosto tardi. La sintassi però l'ha trovata, ed è la stessa che dree aveva proposto circa 15 minuti prima. Un altro punto per dree++. Rimane però da capire cosa fare con questi benedetti typeglob.

Ovviamente, la prima cosa che faccio è digitare perldoc perlmod. Mentre il povero gaspa si dispera...

[18:11] <gaspa> :-|
[18:16] <gaspa> son decisamente confuso...

Ancora qualche minuto di lettura, un po' di "distrazione" lavorativa, ed ecco che mi sembra di aver trovato il bandolo della matassa.

[18:29] <dada> hah!
[18:29] <dada> trovato
[18:29] <dada> $x = 42; print ${*{$main::{x}}{SCALAR}};
[18:29] <dada> scrive 42

La cosa suscita immediatamente la sardonica ammirazione di dree.

[18:29] <dree> elegante
[18:30] <dada> oh, si, se lo dici a qualcuno quasi non sembra che l'hai insulta
        to :-)
[18:30] <gaspa> eh, forse era per quello...
[18:30] <gaspa> :-P

Spiritosi... intanto però funziona. Ma gaspa ancora non si arrende.

[18:30] <gaspa> cmq, ora provo...
[18:31] <gaspa> ma non mi dice il tipo... devo comunque provarli tutti...
[18:31] <dada> questo e' "by design"

Del resto, io l'avevo detto. L'aveva detto pure dakkar. E' nella natura dei typeglob non dare, tout court, informazioni sul "tipo" che giustifica la loro presenza nella symbol table.

Inaspettatamente però, gaspa mi smonta l'adorabile abuso di graffe, semplificando il tutto con la sintassi che stavamo cercando fin dall'inizio (!!!).

[18:32] <gaspa> e poi, scusa, non e' come dire ${$main::{x}}
[18:32] <dada> sostanzialmente si
[18:33] <dada> ma ho usato il typeglob, che e' quello che hai se prendi le chia
        vi di %main::
[18:33] <dada> anzi, i valori, non le chiavi

Lui l'ha capita, finalmente. Io ancora no :-)

Mi sento mancare un po' la terra sotto i piedi, quindi tento ostinatamente di salire in cattedra.

[18:35] <dada> guarda:
[18:35] <dada> while(my($k, $v) = each %main::) { print "$k=", ${*{$v}{SCALAR}}
        , "\n"; }
[18:36] <dada> questo itera nel namespace e scrive i valori scalari associati a
        d ogni simbolo
[18:37] <dada> il fatto e' che in un typeglob non e' che ci sia uno scalare, o
        un array, o un hash
[18:37] <dada> ossia
[18:37] <dada> se tu hai $x e @x
[18:37] <dada> c'e' un solo typeglob per tutti e due
[18:38] <dada> se tu prendi ${*{$main::{x}}{SCALAR}} hai $x
[18:38] <dada> se tu prendi @{*{$main::{x}}{ARRAY}} hai @x
[18:38] <dada> chiaro?

Il che mi sembra una spiegazione pertinente del perché da un typeglob non posso sapere il "tipo". È che, tutto preso dal capire come "dereferenziare" un typeglob, avevo perso un po' di vista l'obiettivo dell'introspezione.

[18:38] <gaspa> si', ma voglio dire
[18:38] <gaspa> while(my($k, $v) = each %main::) { print "$k=", ${main::{v}};}
[18:38] <gaspa> cosi' funziona uguale
[18:39] <gaspa> e mi sembra piu' leggibile.

In realtà, il codice di gaspa avrebbe dovuto essere scritto ${$main::{$k}} (diciamo che "gliela diamo per buona"). Certo, lui a rigor di logica non ha utilizzato il typeglob $v, ma non c'è dubbio che la sintassi sia più gradevole. E del resto, la sua domanda iniziale era "come faccio a prendere il valore di una chiave della symbol table, dato il suo nome?".

Sono alle corde... Non mi resta che contrattaccare :-)

[18:39] <dada> gaspa: VIGLIACCO!

La "singolar tenzone" volge al termine senza vincitori né vinti, come nelle migliori guerre.

[18:39] <gaspa> e comunque non riesco a sapere a priori quali reference sono de
        finiti nel typeglob
[18:39] <gaspa> :-P
[18:39] <dada> :-)

Alla fine, è proprio gaspa a darci la morale di questa storia.

[18:40] <gaspa> comunque abbiamo sviscerato il problema, il che mi fa imparare
        perl...
[18:40] <gaspa> :-D
[18:40] <dada> yep

Tutto è bene quel che finisce bene :-)


Ti è piaciuto questo articolo? Iscriviti al feed!


Inviato da kral il January 9, 2007 6:47 PM

Tanto per fare un po' di bibliografia, nella seconda edizione di Advanced Perl Programming, il primo capitolo è in gran parte dedicato alla symbol table, che Cozens spiega piuttosto bene.

Inviato da dakkar il January 17, 2007 9:44 PM

Aggiungo una postilla, ai posteri (o postumi):

sub types {
my ($name)=@_;
return grep { defined *{$main::{$name}}{$_} }
qw(SCALAR ARRAY HASH CODE IO FORMAT);
}
$main::gino=1;
@main::pino=(1,2);
%main::pino=(1,2);
print "gino: $_\n" for types 'gino';
print "pino: $_\n" for types 'pino';

stampa:

gino: SCALAR
pino: SCALAR
pino: ARRAY
pino: HASH

Donde quel 'pino: SCALAR'? perlref dice che è un caso speciale: gli scalari ci sono sempre, anche se non sono mai stati usati (ovvero, anche solo nominarli via symbol table li autovivifica).

Gli altri tipi non ovvi: CODE esiste se esiste una sub con quel nome; IO se esiste un filehandle con quel nome; FORMAT se esiste un formato con quel nome (ma qualcuno li usa, i formati?)










Devo ricordare i dati personali?






D:
Annunci Google