-+  Associazione
-+  Documenti
-+  Eventi
-+  Community
-+  Blog
-+  Link

Ottobre 2013

Dom Lun Mar Mer Gio Ven Sab
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Cerca






 

« Permutazioni e Perl | Home | IPW 2004: programma online! »

De comparatione
10.06.04

Recentemente sulla mailing list dedicata allo sviluppo del Perl6 è comparsa una interessante domanda: si chiedeva se il Perl6 avrebbe continuato ad avere, come il Perl5, gli operatori eq e == (e quindi tutta la serie degli operatori di comparazione "doppi", gt e >, lt e <, etc.).

La risposta è ovviamente si, e l'interesse sta soprattutto in una serie di riflessioni che l'argomento mi ha portato a fare, e che mi permetto di usare come "pretesto" per infliggervi un breve trattato su come il Perl, al suo interno, organizza e manipola i nostri dati.

Anatomia di una variabile

Sappiamo che il Perl è un linguaggio typeless, nel senso che le variabili non hanno un tipo dichiarato (numero, stringa, etc.) e possono essere considerate, in maniera del tutto trasparente, l'una o l'altra cosa.

Ciò è senz'altro vero, ma esistono tuttavia nel Perl (parliamo del codice C con il quale Perl è implementato) dei tipi di dato, e precisamente 3:

  • IV (Integer Value), un numero intero
  • NV (Numeric Value), un numero decimale
  • PV (Pointer Value), una stringa

Questi tipi (semplifichiamo di molto, ma per ora accontentatevi) corrispondono ai tipi del C int, double e char *.

Quindi, quando nel mio script uso la variabile $x, cos'è in realtà $x? Una di queste tre cose? Non direttamente: le variabili Perl sono SV (Scalar Value), quello che appunto siamo abituati a chiamare "scalare".

Ma di cosa è fatto un SV? Generalmente, di un IV, un NV o un PV, più qualcos'altro a contorno.

A questo punto conviene sospendere un attimo la teoria per vedere come si realizza in pratica quello che abbiamo detto.

Enter Devel::Peek

Per aiutarci nell'indagine dobbiamo fornirci di uno strumento immensamente utile per cominciare a sbirciare sotto le gonne di mamma Perl.

Devel::Peek, che è incluso nella libreria standard del Perl a partire dalla versione 5.6.0, fornisce fondamentalmente una funzione, Dump, che ci permette di passare un SV sotto i raggi X, vedendone così lo scheletro, la composizione interna.

Cominciamo subito con un esempio semplice semplice:

    use Devel::Peek;
    $x = 42;
    print Dump($x);

    SV = IV(0x1827db8) at 0x1823ffc
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 42

Ecco il nostro SV, ed eccolo il nostro IV, che vale 42. FLAGS e REFCNT sono il "contorno" di cui abbiamo accennato prima (quei numeri 0x... sono indirizzi di memoria, potete fare come se non ci fossero per ora).

Facciamo lo stesso con una stringa:

    use Devel::Peek;
    $x = "hello world";
    print Dump($x);
    
    SV = PV(0x2253bc) at 0x182401c
      REFCNT = 1
      FLAGS = (POK,pPOK)
      PV = 0x1828304 "hello world"\0
      CUR = 11
      LEN = 12

Un PV, come vediamo, è in realtà un po' più complesso di un semplice char *. CUR e LEN (più altre cosette qui non riportate) servono al Perl per gestire efficientemente l'allocazione di memoria per le stringhe (questa, però, è un'altra storia).

Ed ora, vediamo di complicare un po' le cose.

Tutti per uno, uno per tutti

Considerate il seguente codice:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e28c
      REFCNT = 1
      FLAGS = (POK,pPOK)
      IV = 42
      PV = 0x22b3f4 "42"\0
      CUR = 2
      LEN = 3

E questo PVIV cos'è? A occhio e croce sembra una cosa che è sia un PV che un IV. E strano a dirsi, è esattamente quel che sembra.

Quando il Perl si trova a dover effettuare delle conversioni fra tipi, sceglie (e questa è una sua caratteristica molto peculiare) di conservare in ogni caso il valore del tipo dal quale converte.

Un SV può avere quindi una doppia identità, numero e stringa. Il Perl tiene traccia nei FLAGS dell'SV di quale (o quali) fra queste identità è valida. Sono le "bandierine" che finiscono per OK: IOK indica che l'IV è valido, POK che il PV è valido e NOK che l'NV è valido (le flag con la p minuscola davanti sono private, quindi fate finta di non averle viste).

Nel nostro caso, dopo l'operazione solo il PV è valido (c'è POK ma non IOK). Ma ha senso una cosa del genere? Sì, se considerate il seguente codice:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    $y++;
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e284
      REFCNT = 1
      FLAGS = (POK,pPOK)
      IV = 42
      PV = 0x22b3f4 "43"\0
      CUR = 2
      LEN = 3

Abbiamo "43" nel PV (l'operatore di auto-incremento è sul valore stringa) e 42 nell'IV, che non è, ovviamente, più valido.

Potrebbe a questo punto venirvi il dubbio: perché mantenere sia un IV che un PV per una sola variabile? Non è un inutile spreco di memoria? Tutt'altro che inutile: innanzitutto liberare memoria è un processo relativamente costoso, in termini di tempo macchina. In secondo luogo, Perl usa ovunque può le conversioni già fatte per "portarsi avanti col lavoro", in un certo senso.

Se al nostro codice aggiungiamo una riga:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    $y++;
    $y = 0+$y;
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e28c
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 43
      PV = 0x22b3f4 "43"\0
      CUR = 2
      LEN = 3

Ecco che abbiamo riutilizzato lo stesso IV che avevamo già allocato.

Tornando a noi...

E' ora di tornare al nostro punto di partenza, eq e ==. Qualche sospetto a questo punto vi sarà forse già venuto, ma lasciate che enunci la cosa come si conviene.

Da un punto di vista semantico, proprio perché le variabili Perl sono typeless (né numero né stringa) acquistano un senso unicamente nel contesto in cui vengono utilizzate, ed è precisamente quello che fanno eq (contesto stringa) e == (contesto numerico).

Da un punto di vista "ontologico", gli operatori di comparazione non sono, come in altri linguaggi, semplici confronti sul valore di una cosa, ma implicano operazioni di conversione, e quindi alterano l'essenza del comparato.

Sorpresi? Beh, sarebbe carino da parte vostra esserlo, perché questo è il punto cruciale del mio articolo :-)

Vediamo dunque che succede quando utilizziamo questi operatori nel codice:

    use Devel::Peek;
    $z = 42;
    print Dump($z);
    1 if $z eq "42";
    print Dump($z);

    SV = IV(0x1828e58) at 0x184f848
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 42
    SV = PVIV(0x2259cc) at 0x184f848
      REFCNT = 1
      FLAGS = (IOK,POK,pIOK,pPOK)
      IV = 42
      PV = 0x22cb04 "42"\0
      CUR = 2
      LEN = 3

Notate la differenza fra prima e dopo l'if? L'operazione di confrontare $z con la stringa "42" l'ha trasformata in un PVIV (e in questo caso, è proprio sia un PV valido che un IV valido).

E tutto ciò è molto sensato. Se ad esempio la quarta riga del nostro ultimo esempio fosse:

    if($z eq "42") { print "Z=$z\n"; }

In questo caso, l'istruzione print si troverebbe $z già convertito in una stringa! Ovviamente, anche print, come eq, trasforma in PV le variabili che interpola nell'output.

Conclusioni

Spero che questo giro di montagne russe fra gli SV vi sia risultato in qualche modo utile. Se non altro, avete conosciuto un nuovo modulo (Devel::Peek) e avete qualche argomento in più da dare a chi vi chiede il perché di eq e ==.

Sappiate, inoltre, che abbiamo appena appena scalfito la superficie di un oceano, quello dei Perl Internals, in cui ci sarebbe da nuotare per anni e anni. Quando avrò trovato il prossimo pretesto, magari, comparirà una seconda puntata di questo articolo :-)

Intanto, se siete interessati, vi lascio qualche riferimento per continuare da soli le vostre indagini:

  • http://www.netthink.co.uk/downloads/internals/ un corso di Simon Cozens sui Perl Internals, uno dei migliori punti di partenza che possiate trovare.
  • http://gisle.aas.no/perl/illguts/ un corposo documento (con illustrazioni!) di Gisle Aas che spiega probabilmente più di quanto vogliate sapere.
  • e naturalmente la documentazione del vostro Perl! In particolare, le pagine perlguts, perlxstut, perlxs, perlcall e perlapi (non vi demoralizzate subito se vi sentite persi, è roba decisamente pesante :-).

cheers,
Aldo

Inviato da dada il 10.06.04 15:45
Ti è piaciuto questo articolo? Iscriviti al feed!

Inviato da Cosimo il 11.06.04 15:19

Ancora una volta grazie!

Mi hai chiarito diversi aspetti, che mi erano un po' "foschi", dato che non sono spesso "in giro" negli internals del perl, anche se avevo familiarita' con i vari IV, NV, HV, ...

Sei riuscito a darmi una veloce infarinata e nel contempo a stimolarmi la curiosità di saperne di più...

Complimenti!

Inviato da Marco Lamberto il 11.06.04 22:52

Complimenti! Ben fatto e sicuramente una lettura interessante che non risparmiero` ad alcuni miei amici ... corro a forwardare l'url! ;)

Inviato da gabriele renzi il 13.06.04 20:16

articoletto molto appetitoso, grazie! :)










Devo ricordare i dati personali?






D:
Sull'autore...
D:
La ML di Perl.it
Iscriviti! mongers@lists.perl.it è la lista ufficiale di Perl Mongers Italia per porre quesiti di tipo tecnico, per rimanere aggiornato su meeting, incontri, manifestazioni e novità su Perl.it.
D:
Annunci Google