NOME |
local()
e my()?
perlfaq7 - Questioni Generali Sul Linguaggio Perl ($Revision: 1.18 $, $Date: 2006/02/12 17:02:20 $)
Questa sezione tratta questioni generali sul linguaggio Perl che non trovano posto in nessuna delle altre sezioni.
Non c'è alcuna BNF ma, se siete di animo particolarmente coraggioso, è possibile arrivarci a tentoni con la grammatica yacc che si trova nel file perl.y dei sorgenti. La grammatica può contare su codice molto astuto per effettuare la divisione in token (tokenizing), preparatevi dunque ad avventurarvi anche nel file toke.c
Citando Chaim Frenkel: ``La grammatica del Perl non può essere ridotta alla BNF. Il lavoro di analisi sintattica che fa il perl è distribuito tra yacc, il lexer e il gioco delle 3 carte''.
Sono specificatori di tipo, come viene spiegato in dettaglio in perldata:
$ per i valori scalari (numeri, stringhe o riferimenti) @ per gli array % per gli hash (array associativi) & per le subroutine (altrimenti dette funzioni, procedure, metodi) * per tutti i tipi di quel nome di simboli. Nella versione 4 si usavano come puntatori, ma nei perl moderni si possono usare solo i riferimenti
Ci sono un paio di altri simboli che è probabile incontrare che non sono specificatori di tipo:
<> sono usate per leggere un record da un filehandle. \ ottiene un riferimento a qualcosa.
Va notato che <FILE> non è uno specificatore di tipo per i file e nemmeno
il nome dell'handle. È l'operatore <>
applicato all'handle FILE.
Legge una linea (o meglio, un record--consultate la voce $/ in perlvar/$/)
dall'handle FILE se in un contesto scalare, oppure tutte le linee se in un
contesto di lista. Quando si apre, si chiude, o si esegue un'operazione
qualunque che non sia <>
su un file, o anche quando si menziona il
filehandle, non si devono usare le parentesi angolari. Queste scritture sono
corrette: eof(FH)
, seek(FH, 0, 2)
e ``copiare da STDIN a FILE''.
Di norma, una parola non posta tra virgolette può rimanere tale, ma
in molti casi probabilmente dovrebbe essere posta tra virgolette (e deve esserlo se use strict
è attivo). La chiave di un hash che consiste in una semplice parola
(che non è il nome di una subroutine definita) e l'operando sinistro
dell'operatore =>
, però, sono trattati come se fossero tra
virgolette:
Questo e` come questo ------------ --------------- $pippo{linea} $pippo{'linea'} pluto => roba 'pluto' => roba
Il punto e virgola finale in un blocco è opzionale, così come la virgola finale in una lista. È buono stile (vedete the perlstyle manpage) metterli, fatta eccezione per il codice costituito da una sola linea:
if ($ops) { exit 1 } @numeri = (1, 2, 3);
if ($ops) { exit 1; } @linee = ( "Li` giunse Beren dal monte imponente", "E tra le fronde e gli alberi vagabondo` disperso", );
Un modo è quello di trattare i valori di ritorno come una lista e di indicizzarla:
$dir = (getpwnam($utente))[7];
Un'altra maniera è quella di usare undef come elemento nella parte sinistra, come in:
($dev, $ino, undef, undef, $uid, $gid) = stat($file);
Potete anche usare uno slice di lista per selezionare solo gli elementi che vi servono:
($dev, $ino, $uid, $gid) = ( stat($file) )[0,1,4,5];
Se state utilizzando Perl 5.6.0 o superiore, la direttiva use warnings
permette di controllare finemente quali avvertimenti vengono generati.
Consultate the perllexwarn manpage per ulteriori dettagli.
{ no warnings; # disabilita temporanemente i warning $a = $b + $c; # questi potrebbero essere undef }
In aggiunta, potete abilitare e disabilitare categorie di warning. Potete togliere le categorie che volete ignorare e potete ancora ottenere altre categorie di warning. Consultate the perllexwarn manpage per i dettagli completi, inclusi i nomi delle categorie e gerarchie.
{ no warnings 'uninitialized'; $a = $b + $c; }
Se avete una versione più vecchia di Perl, la variabile '$^W' (documentata in the perlvar manpage) controlla, al momento dell'esecuzione, i warning per un blocco:
{ local $^W = 0; # disabilita temporanemente i warning $a = $b + $c; # questi potrebbero essere undef }
È da notare che, come per tutte le variabili speciali [quelle
il cui nome è un segno di punteggiatura, NdT], non è possibile
usare my()
su '$^W' ma solo local().
Un'estensione è un modo per chiamare da Perl del codice C compilato. Leggere the perlxstut manpage è un buon modo per imparare di più sulle estensioni.
In realtà non è vero. Tutti gli operatori del C che vengono riprodotti in Perl hanno la stessa precedenza che hanno in C. Il problema si presenta con gli operatori che in C non esistono, in special modo le funzioni che conferiscono un contesto di lista a tutto quello che sta alla loro destra, ad esempio print, chmod, exec e così via. Queste funzioni sono chiamate ``operatori di lista'' e appaiono come tali nella tabella delle precedenze di perlop.
Un errore comune è scrivere:
unlink $file || die "caos";
Esso viene interpretato come:
unlink ($file || die "caos");
Per evitare questo problema, usate delle parentesi in più oppure
usate l'operatore or
che ha una precedenza bassissima:
(unlink $file) || die "caos"; unlink $file or die "caos";
Gli operatori ``in lingua inglese'' (and
, or
, xor
e not
) hanno una
precedenza volutamente inferiore a quella degli operatori di lista, esattamente
per le situazione come quella menzionata sopra.
Un altro operatore con una precedenza sorprendente è l'operatore di
elevamento a potenza. Ha una precedenza superiore anche al meno unario,
facendo sì che -2**2
produca un quattro negativo anzichè
positivo. Inoltre è associativo a destra, e questo vuol dire che
2**3**2
è due elevato alla nona potenza, non otto al quadrato.
Benché abbia la stessa precedenza che ha in C, l'operatore ?:
del
Perl produce un lvalue [un valore che può trovarsi a sinistra di un
operatore di assegnamento, NdT]. Questo codice assegna $x a $a o $b, a seconda del
valore di verità di $forse:
($forse ? $a : $b) = $x;
In generale, non si ``dichiara'' una struttura. Si usa un riferimento ad un hash (di solito, anonimo). Consultate the perlref manpage e the perldsc manpage per i dettagli. Di seguito c'è un esempio:
$persona = {}; # nuovo hash anonimo $persona->{ETA} = 24; # imposto il campo ETA a 24 $persona->{NOME} = "Nat"; # imposto il campo NOME a "Nat"
Se cercate qualcosa un po' più rigoroso, provate the perltoot manpage.
(contributo di brian d foy)
the perlmod manpage, the perlmodlib manpage, the perlmodstyle manpage spiegano i moduli in tutti i sanguinolenti particolari. the perlnewmod manpage dà una breve panoramica del processo insieme con un paio di suggerimenti sullo stile.
Se avete la necessità di includere del codice C o delle librerie di interfaccia C nei vostri moduli, avrete bisogno di h2xs. h2xs creerà la struttura della distribuzione del modulo e gli iniziali file di interfaccia che vi servirranno. the perlxs manpage e the perlxstut manpage spiegano i dettagli.
Se non avete bisogno di usare del codice C, altri strumenti quali ExtUtils::ModuleMaker e Module::Starter potrebbero aiutarvi a creare uno scheletro di distribuzione del modulo.
Potreste anche voler dare un'occhiata al libro di Sam Tregar ``Writing Perl Modules for CPAN'' ( http://apress.com/book/bookDisplay.html?bID=14 ) che è la migliore guida pratica per la creazione di distribuzioni di moduli.
Per una introduzione a classi e oggetti consultate la pagina del manuale the perltoot manpage; consultate anche the perlobj manpage e the perlbot manpage.
Potete usare la funzione tainted()
del modulo Scalar::Util, disponibile
da CPAN (o inclusa con Perl a partire dalla versione 5.8.0).
Consultate inoltre Laundering and Detecting Tainted Data in the perlsec manpage
[``Rendere puliti e rilevare i dati potenzialmente dannosi'', NdT].
Le chiusure sono documentate in the perlref manpage.
Chiusura è un termine dell'informatica con un signficato preciso ma difficile da spiegare. Le chiusure sono implementate in Perl come subroutine anonime che conservano un riferimento persistente a variabili lessicali non più visibili. Queste variabili lessicali, magicamente, fanno riferimento alle variabili che erano visibili quando la subroutine è stata definita (deep binding).
Le chiusure hanno senso in qualunque linguaggio di programmazione nel quale il valore di ritorno di una funzione può essere esso stesso una funzione, come in Perl. Notate che alcuni linguaggi mettono a disposizione le funzioni anonime, ma non sono in grado di fornire chiusure effettive: il Python, per esempio [1]. Per ulteriori informazioni sulle chiusure, fate riferimento ad un qualunque libro sulla programmazione funzionale. Scheme è un linguaggio che non solo supporta, ma incoraggia l'uso di chiusure.
Ecco una classica funzione che genera una funzione:
sub generatore_di_funzione_somma { return sub { shift() + shift() }; }
$funzione_somma = generatore_di_funzione_somma(); $somma = $funzione_somma->(4,5); # $sum ora e` 9.
La chiusura funziona come un template di funzione con dello spazio lasciato libero
per la personalizzazione, da riempire in seguito. La subroutine anonima
restituita da generatore_di_funzione_somma()
non è tecnicamente una
chiusura poichè non fa riferimento ad alcuna variabile lessicale non
più visibile.
Paragonate il caso precedente alla seguente funzione crea_sommatore(), nella quale la funzione anonima che viene restituita contiene un riferimento ad una variabile lessicale non visibile dalla funzione stessa. Questo riferimento richiede che Perl restituisca un'opportuna chiusura, bloccando quindi per sempre il valore che la variabile lessicale aveva quando la funzione è stata creata.
sub crea_sommatore { my $addendo = shift; return sub { shift() + $addendo }; }
$f1 = crea_sommatore(20); $f2 = crea_sommatore(555);
Ora &$f1($n)
è sempre 20 più qualunque $n abbiate passato,
mentre &$f2($n)
è sempre 555 più il valore passato.
Le chiusure sono usate spesso per scopi meno esoterici. Ad esempio, quando volete passare un po' di codice ad una funzione:
my $linea; timeout( 30, sub { $linea = <STDIN> } );
Se il codice fosse stato passato come stringa, '$linea = <STDIN>'
, non
ci sarebbe stato alcun modo, per l'ipotetica funzione timeout(), di accedere
alla variabile lessicale $line visibile dal chiamante.
[1] NdT: questa nozione a proposito di Python non è più vera. A partire dalla versione 2.2 si possono costruire chiusure con variabili di sola lettura. Non conosco gli interpreti Python successivi. Per approfondimenti:
http://www.norvig.com/python-lisp.html
Questo problema è stato risolto nel perl 5.004_05, dunque prevenirlo significa aggiornare la vostra versione di perl. ;)
Il suicidio di variabile si ha quando (temporaneamente o permanentemente) si
perde il valore di una variabile. È causato da questioni di
visibilità frutto dell'interazione tra my()
e local()
o con le
chiusure o con variabili che sono iteratori alias per foreach()
e con
argomenti di subroutine. Di solito era facile perdere inavvertitamente il
valore di una variabile in questa modo, ora però è più
difficile. Prendete questo codice:
my $f = 'pippo'; sub T { while ($i++ < 3) { my $f = $f; $f .= 'pluto'; print $f, "\n" } } T; print "Infine $f\n";
Se state avendo a che fare con il suicidio di variabile, quel my $f
nella subroutine
non tira su una nuova copia di $f
, il cui valore è pippo
. L'output
mostra che all'interno della subroutine, il valore di $f
oltrepassa i limiti
quando non dovrebbe, come in questo output:
pippopluto pippoplutopluto pippoplutoplutopluto Infine pippo
La $f a cui è stato aggiunto ``pluto'' per tre volte dovrebbe essere una
nuova $f. my $f
dovrebbe creare una nuova variabile locale ogni volta che
si compie il ciclo. L'output che ci si aspetta è:
pippopluto pippopluto pippopluto Infine pippo
Fatta eccezione per le espressioni regolari, dovete passare un riferimento a questi oggetti. Consultate Pass by Reference in the perlsub manpage [``Passato per Riferimento'', NdT] per tale particolare questione, e the perlref manpage per informazioni sui riferimenti.
Consultate il sottostante paragrafo ``Passaggio di espressioni regolari'', per avere informazioni sul passaggio di espressioni regolari.
funz( \$un_certo_scalare );
funz( \@un_certo_array ); funz( [ 1 .. 10 ] );
funz( \%un_certo_hash ); funz( { questo => 10, quello => 20 } );
funz( \&una_certa_funzione ); funz( sub { $_[0] ** $_[1] } );
open my $fh, $nomefile or die "Non posso aprire $nomefile! $!"; funz( $fh );
sub funz { my $fh_passato = shift;
my $linea = <$fh>; }
Prima del Perl 5.6, dovevate usare le notazioni *FH
oppure \*FH
.
Queste sono ``typeglob''--per maggiori informazioni, consultate
Typeglobs and Filehandles in the perldata manpage [``Typglob e Filehandle'', NdT] e in modo
particolare Pass by Reference in the perlsub manpage [``Passato per Riferimento'', NdT].
qr//
, passare
stringhe e usare eval in maniera tale che catturi le eccezioni o altrimenti
essere molto, molto furbi.
Ecco un esempio che mostra come si fa a passare una stringa che deve
essere verificata con una espressione regolare, usando qr//
:
sub verifica($$) { my ($val1, $regex) = @_; my $retval = $val1 =~ /$regex/; return $retval; } $corrispondenza = verifica("old McDonald", qr/d.*D/i);
Va notato che qr//
permette di usare i flag alla fine. Quel pattern viene
compilato a tempo di compilazione, benché venga eseguito
successivamente.
La notazione qr//
non è stata introdotta che con la versione 5.005.
In precedenza ci si doveva avvicinare al problema in maniera molto meno
intuitiva. Ecco lo stesso esempio, nel caso non si abbia qr//
:
sub verifica($$) { my ($val1, $regex) = @_; my $retval = eval { $val1 =~ /$regex/ }; die if $@; return $retval; }
$corrispondenza = verifica("old McDonald", q/($?i)d.*D/);
Assicuratevi di non scrivere mai cose del genere:
return eval "\$val =~ /$regex/"; # SBAGLIATO
altrimenti qualcuno potrebbe insinuare comandi di shell a causa della doppia interpolazione dell'eval e della stringa tra doppi apici. Ad esempio:
$pattern_malefico = 'pericolo ${ system("rm -rf * &") } pericolo';
eval "\$stringa =~ /$pattern_malefico/";
Chi preferisce essere molto molto furbo può consultare il volume della O'Reilly ``Mastering Regular Expressions'', di Jeffrey Friedl. La Build_MathMany_Function(), che si trova a pagina 273, è particolarmente interessante. Gli estremi completi di questo testo si trovano in the perlfaq2 manpage.
chiamata_multipla(10, $un_certo_oggetto, "nome_metodo") sub chiamata_multipla { my ($contatore, $oggetto, $metodo) = @_; for (my $i = 0; $i < $contatore; $i++) { $oggetto->$metodo(); } }
Oppure potete usare una chiusura per impacchettare l'oggetto, la chiamata al metodo e gli argomenti:
my $non_manca_niente = sub { $un_certo_oggetto->obfuscate(@args) }; funz($non_manca_niente); sub funz { my $codice = shift; &$codice(); }
Potete inoltre indagare sul metodo can()
della classe UNIVERSAL (inclusa
nella distribuzione standard di perl).
(contributo di brian d foy)
Perl non ha le variabili ``statiche'', alle quali si può avere accesso solo dalla funzione nella quale sono dichiarate. Però potete ottenere lo stesso effetto con le variabili lessicali.
Potete simulare una variabile statica usando una variabile lessicale che
va al di fuori dello scope. In questo esempio, si definisce la subroutine contatore
che usa la variabile lessicale $contatore
. Dato che lo inserite in un blocco BEGIN,
$contatore
è definito a tempo di compilazione, ma esso va anche fuori dallo
scope alla fine del blocco BEGIN. Il blocco BEGIN assicura anche che la subroutine e
il valore che essa usa sia definito a tempo di compilazione di modo che la subroutine
sia pronta all'uso proprio come ogni altra subroutine, e si possa inserire questo codice
nella stessa posizione come per le altre subroutine nel testo del programma (ovvero, di
solito, alla fine del codice). La subroutine contatore
ha ancora un riferimento ai dati e
questa è la sola maniera che avete per accedere al valore (ed ogni volta che lo
fate, ne incrementate il valore). I dati nella porzione di memoria definita da $contatore
è privata per contatore
.
BEGIN { my $contatore = 1; sub contatore { $contatore++ } }
my $partenza = contatore();
.... # codice che chiama contatore();
my $fine = contatore();
Nell'esempio precedente, avete creato una variabile privata per una funzione
perch<eacute> solo una funzione si è il suo riferimento. Potreste definire
funzioni multiple mentre la variabile è nello scope, ed ogni funzione
può condividere la variabile ``privata''. Non è davvero ``statica'' perché
potete accedervi dal di fuori della funzione finché la variabile lessicale è
nello scope e potete anche creare riferimenti ad essa. In questo esempio, incrementa_contatore
e
restituisci_contatore
condividono la variabile. Una funzione incrementa il valore e l'altra
semplicemente lo restituisce. Entrambe possono accedere a $contatore
e visto che esso è
andato al di fuori dello scope, non c'è altro modo per accedervi.
BEGIN { my $contatore = 1; sub incrementa_contatore { $contatore++ } sub restituisci_contatore { $contatore } }
Per dichiarare una variabile privata per un file, potete usare ancora una variabile lessicale. Un file è anche uno scope, dunque una variabile lessicale definita nel file non può essere vista da qualsiasi altro file.
Consultate Persistent Private Variables in the perlsub manpage [``Variabili Private Persistenti'', NdT] per maggiori informazioni. La discussione sulle chiusure in the perlref manpage potrebbe aiutarvi anche se non abbiamo usato le subroutine anonime in questa risposta. Consultate Persistent Private Variables in the perlsub manpage per i dettagli.
local()
e my()?local($x)
salvaguarda il vecchio valore della variabile globale $x
e
le assegna un nuovo valore per la durata della subroutine. Tale valore
è visibile nelle funzioni chiamate da detta subroutine. Tutto
ciò viene fatto al momento dell'esecuzione, per cui si parla di
visibilità dinamica. local()
opera sempre su variabili globali,
che vengono chiamate anche variabili di package o variabili dinamiche.
my($x)
definisce una nuova variabile, visibile solo nella subroutine
corrente. Ciò viene fatto al momento della compilazione, e dunque
si parla di visibilità lessicale o statica. my()
opera su variabili
private, chiamate anche variabili lessicali o, in modo improprio, variabili
con visibilità statica.
Ad esempio:
sub visibile { print "il valore di var e` $var\n"; }
sub dinamica { local $var = 'locale'; # nuovo valore temporaneo per la visibile(); # variabile $var, che resta globale }
sub lessicale { my $var = 'privata'; # nouva variabile privata $vatr. visible(); # (invisibile al di fuori dello scopo della subroutine) }
$var = 'globale';
visibile(); # scrive globale dinamica(); # scrive locale lessicale(); # scrive globale
Si noti come il valore ``privata'' non venga mai scritto. Ciò
perché tale valore di $var è visibile solo all'interno
del blocco della funzione lessicale()
e non nella subroutine chiamata.
In breve, local()
non produce ciò che probabilmente pensate, ossia
una variabile privata o locale. local()
dà un valore temporaneo ad
una variabile globale. my()
è ciò che state cercando se
desiderate ottenere delle variabili private.
Consultate anche le sezioni Private Variables via my() in the perlsub manpage [``Variabili Private tramite my()'', NdT] e <perlsub/``Temporary Values via local()''> [``Valori Temporanei tramite local()'', NdT] per i dolorosi dettagli.
Se conoscete il package, potete nominarlo esplicitamente, come in $Un_Certo_Package::var. Notate che con $::var non si intende la variabile dinamica $var nel package corrente, bensì quella nel package ``main'', come se aveste scritto $main::var.
use vars '$var'; local $var = "globale"; my $var = "lessicale";
print "lessicale e` $var\n"; print "globale e` $main::var\n";
Alternativamente potete usare la direttiva del compilatore our()
per
portare una variabile dinamica nello scope lessicale corrente.
require 5.006; # our() non esiste prima del 5.6 use vars '$var';
local $var = "globale"; my $var = "lessicale";
print "lessicale e` $var\n";
{ our $var; print "globale e` $var\n"; }
Nel deep binding, le variabili lessicali che vengono nominate nelle procedure anonime sono le stesse che si trovavano nello scope quando la procedura è stata creata. Nello shallow binding, esse sono una qualsiasi variabile con lo stesso nome, a cui capita di trovarsi nello scope quando la procedura viene chiamata. Il Perl usa sempre il deep binding delle varaibili lessicali (cioè quelle create con my()). Ad ogni modo, le variabili dinamiche (dette anche globali, locali, o variabili package) sono effettivamente fatte oggetto di shallow binding. Considerate questa un'ulteriore ragione per non usarle. Guardate la risposta a Cos'è una chiusura?
my()
e local()
conferiscono un contesto di lista alla parte destra
dell'assegnamento =
. L'operazione di lettura <FH>, come molti altri
operatori e funzioni del Perl, è sensibile al contesto in cui
è stata chiamata, e si comporta di conseguenza. Generalmente la
funzione scalar()
può essere d'aiuto. Questa funzione non ha alcun
effetto sui dati medesimi (contrariamente alla credenza popolare) ma dice
ai suoi argomenti di seguire il comportamento che essi devono avere in un
contesto scalare, qualunque esso sia. Se la funzione non ha un comportamento
scalare definito, questa tecnica naturalmente non vi aiuterà (come
nel caso di sort()).
Per forzare un contesto scalare in questo caso specifico, ad ogni modo, dovete semplicemente togliere le parentesi:
local($pippo) = <FILE>; # SBAGLIATO local($pippo) = scalar(<FILE>); # ok local $pippo = <FILE>; # giusto
Probabilmente dovreste usare variabili lessicali, comunque, ma la sostanza non cambia:
my($pippo) = <FILE>; # SBAGLIATO my $pippo = <FILE>; # giusto
Perché volete fare questo? :-)
Se si vuole sovrapporre una funzione predefinita, come open(), allora si dovrà importare la nuova definizione da un differente modulo. Consultate Overriding Built-in Functions in the perlsub manpage [``Sovrapporre le Funzioni Predefinite'', NdT]. C'è anche un esempio in Class::Template in the perltoot manpage.
Se volete sovraccaricare un operatore Perl, come +
o **
, allora
troverete conveniente usare la direttiva use overload, documentata in
the overload manpage.
Se state parlando di oscure chiamate a metodi in classi genitore, consultate Overridden Methods in the perltoot manpage [``Metodi Sovrapposti'', NdT].
Quando chiamate una funzione come &pippo
, permettete che la funzione
acceda ai valori correnti contenuti in @_ ed evitate di prendere in
considerazione i prototipi. La funzione non otterrà un
@_ vuoto--otterrà il vostro! [l'array del chiamante, NdT]
Benché strettamente parlando non si possa dire un bug (è
documentato in questo modo in perlsub), nella maggior parte dei casi
sarebbe difficile considerarlo una caratteristica.
Quando chiamate una funzione come &pippo()
, allora ottenete un
nuovo @_, ma l'uso dei prototipi viene anche in questo caso aggirato.
Normalmente le funzioni si chiamano usando pippo()
. Potete omettere
le parentesi solo se la funzione è già nota al compilatore
perché ne ha già visto la definizione (usando use
e non
require
), o grazie ad un riferimento in avanti, oppure grazie ad una
dichiarazione use subs. Anche in questo caso otterrete un nuovo @_ senza
nessuno dei vecchi valori che spunta là dove non dovrebbe.
Viene spiegato più approfonditamente in the perlsyn manpage. In sintesi, non c'è un'istruzione case ufficiale a causa della varietà di test che sono possibili in Perl (confronto numerico, confronto tra stringhe, confronto tra glob, pattern matching, operatori di confronto con più significati, ...). Larry non è riuscito a decidere quale fosse il modo migliore per farlo, e quindi lo ha lasciato da parte, benché sia nella lista dei desideri già dalla prima versione di Perl.
A partire da Perl 5.8 per avere switch e case si deve usare l'estensione Switch:
use Switch;
e si avranno switch e case. Non è veloce quanto potrebbe esserlo, poiché non è realmente parte del linguaggio (è fatto usando i source filter) ma è disponibile, ed è molto flessibile.
Se però si vuole usare Perl puro, la risposta generale è scrivere un construtto come questo:
for ( $variabile_da_verificare ) { if (/pat1/) { } # fai qualcosa elsif (/pat2/) { } # fai qualcos'altro elsif (/pat3/) { } # fai qualcos'altro else { } # default }
Ecco un semplice esempio di switch basato su pattern matching, questa volta incolonnato in maniera tale che assomigli maggiormente ad una istruzione switch. Realizzeremo un costrutto condizionale a più vie basato sul tipo di riferimento memorizzato in $ref:
SWITCH: for (ref $ref) {
/^$/ && die "non e` un riferimento";
/SCALAR/ && do { print_scalar($$ref); last SWITCH; };
/ARRAY/ && do { print_array(@$ref); last SWITCH; };
/HASH/ && do { print_hash(%$ref); last SWITCH; };
/CODE/ && do { warn "non posso stampare un riferimento a funzione"; last SWITCH; };
# DEFAULT
warn "Tipo definito dall'utente, tralasciato";
}
Consultate Basic BLOCKs and Switch Statements in the perlsyn manpage [``BLOCK di Base e Istruzioni Switch'', NdT] per trovare molti altri esempi in questo stile.
A volte potreste dover modificare la posizione della costante e della
variabile. Ad esempio, poniamo che vogliate sapere quale tra tante risposte
avete ricevuto, ma in maniera non dipendete dalle maiuscole/minuscole, e permettendo abbreviazioni.
Potreste usare la seguente tecnica se le stringhe cominciano tutte con
caratteri diversi, o se volete ordinare le corrispondenze in maniera tale che
una abbia la precedenza sull'altra, così come in questo caso SEND
ha la precedenza su STOP
:
chomp($answer = <>); if ("SEND" =~ /^Q$answer/i) { print "L'azione e` send\n" } elsif ("STOP" =~ /^Q$answer/i) { print "L'azione e` stop\n" } elsif ("ABORT" =~ /^Q$answer/i) { print "L'azione e` abort\n" } elsif ("LIST" =~ /^Q$answer/i) { print "L'azione e` list\n" } elsif ("EDIT" =~ /^Q$answer/i) { print "L'azione e` edit\n" }
Un approccio del tutto differente consiste nel creare un hash di riferimenti a funzione:
my %commands = ( "felice" => \&gioia, "triste" => \&grigiore, "finito" => sub { die "Ci vediamo!" }, "matto" => \&rabbia, );
print "Come stai? "; chomp($string = <STDIN>); if ($commands{$string}) { $commands{$string}->(); } else { print "Non esiste questo comando: $string\n"; }
Il metodo AUTOLOAD, discusso in Autoloading in the perlsub manpage e in AUTOLOAD: Proxy Methods in the perltoot manpage [``AUTOLOAD: Metodi Proxy'', NdT], vi consente di catturare le chiamate a funzioni e metodi indefiniti.
Quando invece si tratta di variabili indefinite che genererebbero un
avvertimento in presenza di use warnings
, potete promuovere l'avvertimento
in un errore.
use warnings FATAL => qw(uninitialized);
Alcune ragioni possibili: il meccanismo dell'ereditarietà si è
confuso, avete scritto male il nome del metodo, oppure l'oggetto è
del tipo sbagliato. Fate riferimento a the perltoot manpage per dettagli su ciascuno
dei casi menzionati. Potreste inoltre usare print ref($object)
per sapere
qual è la classe nella quale $oject
è stato sottoposto
alla funzione bless().
Un'altra possibile fonte di problemi è l'uso della sintassi
indiretta per gli oggetti (ad esempio trova Guru "Samy"
) sul nome di una
classe prima che Perl abbia visto che il relativo package esiste. È
più saggio assicurarsi che tutti i vostri package siano definiti,
prima di usarli. Verrà fatto se usate l'istruzione use
anziché require
. Altrimenti, assicuratevi di usare la ``notazione
con freccia'' (ad esempio Guru->trova( "Samy" )
). La notazione per gli
oggetti è spiegata in the perlobj manpage.
Assicuratevi di leggere the perlmod manpage a proposito della creazione dei moduli, e Method invocation in the perlobj manpage [``Invocazione di Metodi'', NdT] a proposito dei pericoli degli oggetti indiretti.
In un programma generico, per conoscere il package dell'unità di compilazione corrente potete fare in questo modo:
my $nome_package = __PACKAGE__;
Ma se si tratta di un metodo e volete stampare un messaggio d'errore che menzioni il tipo di oggetto sul quale è stato invocato (che non è necessariamente quello in cui il metodo è stato compilato):
sub metodo { my $self = shift; my $class = ref($self) || $self; warn "Sono stato chiamato da un oggetto $class"; }
Potete usare il POD incorporato nel codice per mettere quest'ultimo da parte.
Racchiudete i blocchi che volete commentare tra etichette POD. La direttiva
<=begin> contrassegna una sezione per uno specifico formattatore. Usate il
formato comment
, che nessun formattatore dovrebbe pretendere di saper capire
(per scelta). Contrassegnate la fine del blocco con <=end>.
# il programma e` qui
=begin comment
Tutte queste cose
qui saranno ignorate da tutti
=end comment
=cut
# il programma continua
Le direttive pod non possono andare proprio dappertutto. Dovete inserire una direttiva pod dove il parser si aspetta una nuova istruzione, non semplicemente nel mezzo di una espressione o di qualche altra produzione grammaticale.
Consultate the perlpod manpage per maggiori dettagli.
Usate questo codice, fornito da Mark-Jason Dominus:
sub scrub_package { no strict 'refs'; my $pack = shift; die "Non si dovrebbe eliminare il package main" if $pack eq "" || $pack eq "main"; my $stash = *{$pack . '::'}{HASH}; my $nome; foreach $name (keys %$stash) { my $nomecompleto = $pack . '::' . $nome; # Elimino tutto con quel nome undef $$nomecompleto; undef @$nomecompleto; undef %$nomecompleto; undef &$nomecompleto; undef *$nomecompleto; } }
Oppure, se state usando una versione recente di Perl, al suo posto potete semplicemente usare la funzione Symbol::delete_package() .
I principianti spesso ritengono di voler avere una variabile che a sua volta contiene il nome di una variabile.
$fred = 23; $nome_variabile = "fred"; ++$$nome_variabile; # $fred adesso vale 24
A volte funziona, ma è un'idea molto sbagliata, per due ragioni.
La prima ragione è che funziona solo con variabili globali. Questo vuol dire che se nell'esempio precedente $fred fosse una variabile lessicale creata con my(), il codice non funzionerebbe per niente: accedereste accidentalmente alla globale, saltando a piè pari la variabile lessicale privata. Le variabili globali non vanno bene perché possono facilmente dar luogo a conflitti accidentali, e in generale rendono il codice confuso e non scalabile.
I riferimenti simbolici sono proibiti quando la direttiva use strict
è
attiva. Non sono veri riferimenti e di conseguenza non sono soggetti al
conteggio dei riferimenti e alla garbage collection.
L'altra ragione per cui usare una variabile per mantenere il nome di un'altra
variabile è una cattiva idea è che la questione spesso
scaturisce da una mancanza di comprensione delle strutture dati Perl, in
particolare degli hash. Se usate i riferimenti simbolici di fatto state
usando l'hash che contiene la tabella dei simboli del package
come %main::
) anziché un hash definito dall'utente. La soluzione
è usare un hash vostro oppure un riferimento vero e proprio.
$VARIABILI_UTENTE{"fred"} = 23; $nome_variabile = "fred"; $VARIABILI_UTENTE{ $nome_variabile }++; # non $$nome_variabile++
Stiamo usando l'hash %VARIABILI_UTENTE anziché i riferimenti simbolici. A volte capita durante la lettura di stringhe inserite dall'utente che contengono riferimenti a variabili che si vogliono espandere con i valori delle variabili del programma. Anche questa è una cattiva idea, poiché confonde gli spazi dei nomi indirizzabili dal programma e dall'utente. Anzichè leggere una stringa ed espanderla con gli effettivi contenuti delle variabili proprie del programma:
$str = 'qui ci sono $fred e $barney'; $str =~ s/(\$\w+)/$1/eeg; # c'e` bisogno di un doppio eval
sarebbe meglio mantenere un hash come %VARIABILI_UTENTE e far sì che i riferimenti puntino in effetti ai contenuti di questo hash:
$str =~ s/\$(\w+)/$VARIABILI_UTENTE{$1}/g; # nessun /e
È più veloce, più pulito e più sicuro dell'approccio precedente. Naturalmente, non c'è bisogno del simbolo del dollaro. Potete usare uno schema personale per rendere le cose meno confuse, come ad esempio dei simboli di percentuale usati a mo' di parentesi, ecc.
$str = 'qui ci sono %fred% e %barney%'; $str =~ s/%(\w+)%/$VARIABILI_UTENTE{$1}/g; # nessun /e
Un'altra ragione per cui le persone a volte pensano di volere una variabile che contiene il nome di una variabile è che non sanno come costruire opportune strutture usando gli hash. Ad esempio, diciamo che in un programma si vogliono due hash: %fred e %barney e si vuole adoperare un altro scalare per fare riferimento ad esse per nome.
$nome = "fred"; $$nome{MOGLIE} = "wilma"; # imposta %fred
$nome = "barney"; $$nome{MOGLIE} = "betty"; # imposta %barney
C'è ancora un riferimento simbolico, ed è ancora afflitto dai problemi elencati sopra. Sarebbe di gran lungo meglio scrivere:
$gente{"fred"}{MOGLIE} = "wilma"; $gente{"barney"}{MOGLIE} = "betty";
E usate solo un hash a più livelli.
Le uniche occasioni in cui dovete assolutamente usare i riferimenti simbolici sono quelle in cui ci si deve riferire alla tabella dei simboli. Potrebbe essere perché si ha a che fare con qualcosa per cui non è possibile avere un riferimento, ad esempio il nome di un formato. Questa pratica potrebbe essere importante anche per le chiamate ai metodi, visto che per essere risolte, passano attraverso la tabella dei simboli.
In questi casi, dovreste disattivare strict 'refs'
temporaneamente in
maniera tale da poter trafficare con la tabella dei simboli. Ad esempio:
@colori = qw(rosso blu verde giallo arancione porpora viola); for my $nome (@colori) { no strict 'refs'; # chiudi un occhio per tutto il blocco *$nome = sub { "<FONT COLOR='$nome'>@_</FONT>" }; }
Tutte queste funzioni (rosso(), blu(), verde(), ecc.) sembrano separate, ma il codice della chiusura è stato compilato, in realtà, una volta sola.
Quindi, a volte potreste voler usare dei riferimenti simbolici per manipolare
direttamente la tabella dei simboli. Non importa se si tratta di format,
handle o subroutine, poiché sono sempre globali -- non potete usare
my()
su di loro. Per gli scalari, gli array e gli hash, però -- e
di solito per le subroutine --, probabilmente volete usare solo hard reference.
(contributo di brian d foy)
Il messaggio ``bad interpreter'' deriva dalla shell, non dal perl. Il messaggio vero e proprio può variare a seconda della vostra piattaforma, shell e impostazione del locale.
Se vedete ``bad interpreter - no such file or directory'' [``interprete non valido - file o directory non presente'', NdT], la prima linea nel vostro script perl (la ``shebang'') non contiene il corretto percorso al perl (oppure qualsiasi altro programma in grado di eseguire degli script). Talvota questo avviene quando spostate lo script da una macchina ad un'altra e ogni macchina ha un differente percorso al perl---per esempio /usr/bin/perl piuttosto che /usr/local/bin/perl per esempio. Potrebbe anche indicare che la macchina sorgente abbia terminatori di linea del tipo CRLF e la macchina destinazione abbia solo i LF: la shell cerca di trovare /usr/bin/perl<CR>, ma non ci riesce.
Se vedete ``bad interpreter: Permission denied'' [interprete non valido: Permesso negato``, NdT], avete bisogno di rendere eseguibile il vostro script.
In entrambi i casi, dovreste essere ancora in grado di eseguire gli script, esplicitamente con perl:
% perl script.pl
Se ottenete un messaggio come ``perl: command not found'' [``perl: comando non trovato'', NdT], perl non è nel vostro PATH [percorso, NdT], che potrebbe anche significare che la posizione del perl non è dove vi aspettate, dunque sarà necessario modificare la vostra shebang.
Copyright (c) 1997-2006 Tom Christiansen, Nathan Torkington e altri autori menzionati. Tutti i diritti riservati.
Questa documentazione è libera; potete ridistribuirla e/o modificarla secondo gli stessi termini applicati al Perl.
Indipendentemente dalle modalità di distribuzione, tutti gli esempi di codice in questo file sono rilasciati al pubblico demanio. Potete, e siete incoraggiati a farlo, utilizzare il presente codice o qualunque forma derivata da esso nei vostri programmi per divertimento o per profitto. Un semplice commento nel codice che dia riconoscimento alle FAQ sarebbe cortese ma non è obbligatorio.
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perlfaq7
Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
Traduzione a cura di Michele Beltrame.
Revisione a cura di dree.
NOME |