-+  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






 

« Per i solutori più che abili | Home | Italian Code Jam, aggiornamento »

Pagina 46 - post mortem di "Per i solutori più che abili"
23.08.04

Visto che nessun altro si cimenta nella soluzione del problema proposto nel post Per i solutori più che abili (complici forse le vacanze estive?), mi permetto di portarvi alla fatidica pag. 46 di enigmistica memoria, per svelare alfine l'arcano.

Innanzitutto riportiamo, per comodità, l'enunciato con cui abbiamo proposto il giochino:

[...] mettete del codice al posto di XXX (e solo lì), in maniera tale che il programma stampi "ok".

XXX;
undef and print "ok";

Vi sono alcune soluzioni "fregatura" al quesito, riportate ad esempio da gmax e larsen. Un'altra piuttosto ingegnosa, se la volete, (opera di ikegami) è questa:

    print map eval,grep/"/,qw;
    undef and print "Ok";

Tutte queste "soluzioni" barano in quanto tendono ad assegnare un valore diverso al punto e virgola che separa il nostro fatidico undef and print "ok" rispetto al suo naturale significato di separatore fra istruzioni. In altre parole, non è esplicito nell'esposizione del problema, ma il codice precedente dovrebbe essere indipendente dall'istruzione seguente, o se preferite, deve poter essere compilato come istruzione a sé.

Vi sono poi soluzioni, come quella di larsen, che pur soddisfacendo anche questo requisito, riescono comunque ad esser fregatura. Semplicemente perché non affrontano il cuore del problema, lo aggirano.

Veniamo dunque al cuore, che affrontiamo, colpiamo, e su cui infieriamo pure, non per sadismo ma perché grandi sono le conoscenze che ce ne possono venire.

Premetto però che io stesso in primis non sono stato capace di risolvere il quesito, la soluzione la trovate su questa pagina (tra parentesi, prima della pubblicazione sul nostro blog, era il primo risultato riportato da Google cercando per "undef and print ok" :-).

Vi è dunque un solo modo per cui il valutare undef, di per sé, possa ritornare un valore vero, tale da indurre il seguente print a stampare "ok". E tale modo è, per quanto blasfemo possa suonare, di ridefinire undef in modo che abbia un valore vero.

Notate il bold su "valore": uno dei primi tentativi fatti da me, e da altri, è stato quello di tentare di ridefinire l'istruzione undef, come riportata da perlfunc:

  • undef ESPR
  • undef

Rende indefinito il valore di ESPR [...] Potete omettere l'ESPR, in tal caso non viene reso indefinito niente, ma otterrete ancora un valore indefinito [...]

Per chi non lo sapesse, è possibile ridefinire un'istruzione Perl con qualcosa di simile al seguente codice:

    # file RidefinisciInt.pm
    package RidefinisciInt;
    require Exporter;
    @ISA = qw(Exporter);
    @EXPORT = qw(int);
    use subs qw(int);
    sub int { return scalar(reverse($_[0])); }
    1;

    # file test.pl
    use RidefinisciInt;
    print int(3.1415927);

L'output di test.pl è un sibillino 7295141.3. Provare per credere :-)

Detto questo, purtroppo undef non fa parte dell'insieme di istruzioni Perl che è possibile ridefinire in tal modo. E anche se lo fosse, semplicemente questa non è la strada giusta. Se leggete bene la seconda frase citata da perlfunc, undef senza un argomento restituisce un valore indefinito. È quello che succede quando scriviamo:

    my $x = undef;

Qui non stiamo utilizzando l'istruzione undef, in realtà, bensì il valore undef. Di fatto, il codice del nostro problema potrebbe essere considerato equivalente a:

    my $x = undef; $x and print 'ok';

Nel qual caso, basterebbe ridefinire $x e il problema sarebbe risolto. Però viene utilizzato undef tout-court. Come ridefinirlo quindi? La cosa più banale che viene in mente è la seguente:

    undef = 42;
    undef and print 'ok';

Non funzionerà mai, giusto? Giusto, ma se guardiamo abbastanza attentamente al perché non funziona, ne possiamo trarre un indizio molto prezioso:

    C:\dada>perl -e "undef = 42; undef and print 'ok';"
    Modification of a read-only value attempted at -e line 1.

L'errore è Modification of a read-only value: bingo! Questo ci conferma che undef è un value. Ora, non resta che renderlo non read-only.

E qui ci addentriamo, metaforicamente parlando, nel ventre della balena. Chi ha avuto modo di cimentarsi con gli Internals del Perl (moduli XS e compagnia bella) saprà per certo che esiste, ed è documentato nelle API, il valore PL_sv_undef, del quale perlapi.pod dice:

  PL_sv_undef

  This is the undef SV. Always refer to this as &PL_sv_undef.
          SV      PL_sv_undef

Se siete attenti lettori di questo blog, inoltre, forse avrete letto il mio articolo De comparatione, in cui introducevo il modulo Devel::Peek. Rinfreschiamoci dunque la memoria e osiamo:

    C:\dada>perl -MDevel::Peek -e "print Dump(undef);"
    SV = NULL(0x0) at 0x224c8c
	  REFCNT = 2147483624
      FLAGS = (READONLY)

Ecco che abbiamo la prova inconfutabile che undef è un SV, ossia una variabile. Solo che fra i suoi flag c'è appunto quel READONLY che ci dà noia.

Esiste però, per quanto non documentato da nessuna parte che io sappia, un modulo chiamato Internals. Non è un file PM come gli altri, è compilato direttamente nell'eseguibile del Perl (se siete curiosi, date un'occhiata a universal.c nei sorgenti del Perl). Il modulo mette a disposizione alcune funzioni, che potete visualizzare così:

    C:\dada>perl -MData::Dumper -e "print Dumper(\%Internals::);"
    $VAR1 = {
          'SvREFCNT' => *Internals::SvREFCNT,
          'hv_clear_placeholders' => *Internals::hv_clear_placeholders,
          'hash_seed' => *Internals::hash_seed,
          'SvREADONLY' => *Internals::SvREADONLY,
          'HvREHASH' => *Internals::HvREHASH,
          'rehash_seed' => *Internals::rehash_seed
    };

A seconda della versione del Perl che utilizzate (io sto usando una 5.8.3) il risultato potrebbe essere differente. Per inciso, non so bene in quale versione del Perl sia stato introdotto il modulo Internals, di sicuro non esisteva nella 5.6.1. Tra le funzioni del modulo, quella che ci interessa (sulle altre non ci soffermiamo, almeno per questa volta :-) è, ovviamente, SvREADONLY. Andando a sbirciare nei sorgenti, intuiamo che questa funzione è in grado di impostare il flag omonimo al valore che noi passiamo come secondo argomento. Basta chiamarla su undef, quindi, e dovremmo essere in grado di eliminare il fastidioso flag! Proviamo insieme:

    C:\dada>perl -e "Internals::SvREADONLY(\undef, 0);"
    Type of arg 1 to Internals::SvREADONLY must be one of [$%@] (not 
    single ref constructor) at -e line 1, near "0)"
    Execution of -e aborted due to compilation errors.

Ouch. Sembra che ci sia un piccolo ostacolo da superare. Ma non diamoci per vinti, e facciamo invece appello a tutte le nostre conoscenze. Se diamo un'occhiata al prototipo con cui è definita Internals::SvREADONLY, vediamo che accetta una reference ad una variabile (scalare, array o hash) e un valore scalare opzionale:

    C:\dada>perl -e "print prototype('Internals::SvREADONLY');"
    \[$%@];$

undef, però, non è né uno scalare, né un hash, né un array. È qualcosa di più simile ad una costante letterale, pur essendo un SV internamente. Però, noi sappiamo anche che in Perl è possibile cortocircuitare i prototipi utilizzando la notazione &funzione. Riproviamo dunque:

    C:\dada>perl -MDevel::Peek -e "&Internals::SvREADONLY(\undef, 0); 
            print Dump(undef);"
    SV = NULL(0x0) at 0x224cbc
      REFCNT = 2147483624
      FLAGS = ()

Ecco che abbiamo finalmente eliminato il flag! Da qui, la strada è tutta in discesa, ed arriviamo alla tanto agognata soluzione, che vi presento in tutta la sua gloria:

    &Internals::SvREADONLY(\undef, 0); undef = 42;
    undef and print 'ok';

Vittoria! La soluzione, avete visto, è tutt'altro che banale, e presuppone conoscenze che vanno molto al di là dell'utilizzo "normale" del Perl. Spero comunque che il giro vi abbia insegnato qualcosa, o vi abbia perlomeno divertito.

Non dovrebbe servire dirlo, ma lo diciamo lo stesso: una cosa del genere è assolutamente illegale nonché immorale, secondo tutti i canoni del buon senso, del buon gusto e della buona programmazione. È una brillante soluzione ad un brillante problema, ma non ha (né deve avere) applicazioni pratiche. Vi basti pensare che, dopo la prima riga della soluzione, il codice my $x = undef assegnerà 42 ad $x, e voi non volete che questo possa succedere, vero? Per non parlare del paradossale defined(undef), che risulta essere vero dopo le nostre manipolazioni! (curiosamente, l'istruzione undef $x continua a funzionare, ossia rende $x non definita, come anche il summamente paradossale: undef undef :-).

Buon fine vacanze a tutti!

cheers,
Aldo

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

Inviato da dada il 24.08.04 19:47

Ho dimenticato, nel mio post, di menzionare il fatto che il modulo Internals (o meglio, una versione di tale modulo) la trovate su CPAN.

Quello del CPAN ha una sintassi leggermente diverso da quella che vi ho fatto vedere, ma le funzionalità sono più o meno le stesse (molto probabilmente, il modulo di Beyer è stato utilizzato come base per quello che attualmente è integrato nell'eseguibile del Perl).

Il modulo è lì dal 2001, quindi molto probabilmente funzionerà anche con versioni "antiche" del Perl. Potrebbe tornarvi utile per risolvere il quesito con un Perl 5.6.0, ad esempio :-)

cheers,

Aldo

Inviato da [LucaS] il 14.09.04 10:09

Bhe innanzi tutto complimenti per lo splendido blog!
Ho anch'io la mia soluzione... se volete fregatura! ma in fin dei conti una soluzione è sempre una soluzione :O)

s/undef/1/ && eval while <DATA>;
__DATA__;
undef and print "ok";

[LucaS]

Inviato da Cosimo il 29.05.08 02:23

Che figata spaziale sto articolo...

Inviato da polettix il 23.06.08 03:25

Divertentissimo ed illuminante, come sempre! dada++










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