NOME
perlfaq7 - Questioni Generali Sul Linguaggio Perl ($Revision: 1.15 $, $Date: 2005/08/01 10:35:20 $)
DESCRIZIONE
Questa sezione tratta questioni generali sul linguaggio Perl che non trovano posto in nessuna delle altre sezioni.
È possibile ottenere una BNF/yacc/RE del linguaggio Perl?
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".
Cosa sono tutti quei segni di punteggiatura $@%&*, e come faccio a sapere quando usarli?
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".
Devo sempre/non devo mai mettere tra virgolette le mie stringhe, oppure usare i punti e virgola o le virgole?
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 perlstyle) 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",
);
Come tralascio alcuni valori di ritorno?
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];
Come posso disabilitare temporaneamente i warning?
Se state utilizzando Perl 5.6.0 o superiore, la direttiva use warnings
permette di controllare finemente quali avvertimenti vengono generati. Consultate perllexwarn 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 perllexwarn 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 perlvar) 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().
Cos'è un'estensione (extension)?
Un'estensione è un modo per chiamare da Perl del codice C compilato. Leggere perlxstut è un buon modo per imparare di più sulle estensioni.
Perché gli operatori del Perl hanno una precedenza diversa rispetto a quelli del C?
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;
Come dichiaro/creo una struttura?
In generale, non si "dichiara" una struttura. Si usa un riferimento ad un hash (di solito, anonimo). Consultate perlref e perldsc 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 perltoot.
Come si crea un modulo?
(contributed by brian d foy)
perlmod, perlmodlib, perlmodstyle spiegano i moduli in tutti i sanguinolenti particolari. perlnewmod 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. perlxs e perlxstut 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 distribuizioni di moduli.
Come faccio a creare una classe?
Per una introduzione a classi e oggetti consultate la pagina del manuale perltoot; consultate anche perlobj e perlbot.
Come faccio a stabilire se una variabile è potenzialmente dannosa?
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 perlsec ["Rendere puliti e rilevare i dati potenzialmente dannosi", NdT].
Che cos'è una chiusura?
Le chiusure sono documentate in perlref.
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
Cos'è il suicidio di variabile e come si può prevenire?
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";
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). Ad ogni modo non è così. Questo era un errore, ora risolto nelle ultime versioni (testato con 5.004_05, 5.005_03 e 5.005_56).
Come faccio a passare/restituire una {Funzione, FileHandle, Array, Hash, Metodo, Espressione Regolare}?
Fatta eccezione per le espressioni regolari, dovete passare un riferimento a questi oggetti. Consultate "Pass by Reference" in perlsub ["Passato per Riferimento", NdT] per tale particolare questione, e perlref per informazioni sui riferimenti.
Consultate il sottostante paragrafo ``Passaggio di espressioni regolari'', per avere informazioni sul passaggio di espressioni regolari.
- Passaggio di variabili e funzioni
-
Le variabili e le funzioni sono abbastanza facili da passare: limitatevi a passare un riferimento ad una variabile esistente o anonima, oppure ad una funzione:
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] } );
- Passaggio di filehandle
-
Dal perl 5.6, potete rappresentare i filehandle con variabili scalari che trattare come ogni altro scalare.
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 perldata ["Typglob e Filehandle", NdT] e in modo particolare "Pass by Reference" in perlsub ["Passato per Riferimento", NdT]. - Passaggio di espressioni regolari
-
Per passare espressioni regolari, è necessario usare una versione di Perl sufficientemente recente da supportare il costrutto
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 notazioneqr//
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 abbiaqr//
: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 perlfaq2.
- Passaggio di metodi
-
Per passare un metodo ad una subroutine, potete fare così:
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).
Come si crea una variabile statica?
Come per molte cose che riguardano il Perl, c'è più di un modo per farlo. Ciò che in altri linguaggi è una "variabile statica" in Perl potrebbe essere una variabile privata ad una funzione (una variabile che è visibile in una sola funzione, e mantiene il suo valore tra una chiamata e l'altra), oppure una variabile privata ad un file (visibile soltanto alle funzioni contenute nel file in cui essa è stata dichiarata).
Ecco il codice per implementare una variabile privata ad una funzione:
BEGIN {
my $contatore = 42;
sub contatore_precedente { return --$contatore }
sub contatore_successivo { return $contatore++ }
}
Ora contatore_precedente() e contatore_successivo() condividono una variabile privata $contatore, che è stata inizializzata durante la compilazione.
Per dichiarare una variabile privata ad un file, farete ancora uso di my(), ponendo la dichiarazione all'inizio del file, nel blocco più esterno. Ponete che questo sia il file Pax.pm:
package Pax;
my $inizio = scalar(localtime(time()));
sub cominciato { return $inizio }
Quando use Pax
oppure require Pax
caricano questo modulo, la variabile sarà inizializzata. Non sarà distrutta dal garbage collector come avviene per la maggior parte delle variabili che non sono più visibili, perchè la funzione cominciato() se ne occupa, ma nessun altro può ispezionarne il contenuto. Non si chiama $Pax::inizio perchè la sua visibilità non è legata al package. La sua visibilità coincide con il file. Potenzialmente potreste avere diversi package nello stesso file che accedono alla stessa variabile, ma un altro file con lo stesso package non potrebbe accederle.
Consultate "Persistent Private Variables" in perlsub ["Variabili Private Persistenti", NdT] per dettagli.
Qual è la differenza tra visibilità dinamica e visibilità lessicale (o statica)? Qual è la differenza tra 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 perlsub ["Variabili Private tramite my()", NdT] e <perlsub/"Temporary Values via local()"> ["Valori Temporanei tramite local()", NdT] per i dolorosi dettagli.
Come posso accedere ad una variabile dinamica quando è visibile una variabile lessicale con lo stesso nome?
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";
}
Qual è la differenza tra il deep binding (legame profondo) e lo shallow binding (legame superficiale)?
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?"
Perché "my($pippo) = <FILE>;" non funziona bene?
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
Come si fa a ridefinire una funzione, operatore o metodo predefinito?
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 perlsub ["Sovrapporre le Funzioni Predefinite", NdT]. C'è anche un esempio in "Class::Template" in perltoot.
Se volete sovraccaricare un operatore Perl, come +
o **
, allora troverete conveniente usare la direttiva use overload, documentata in overload.
Se state parlando di oscure chiamate a metodi in classi genitore, consultate "Overridden Methods" in perltoot ["Metodi Sovrapposti", NdT].
Qual è la differenza tra il chiamare una funzione con &pippo e con pippo()?
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.
Come faccio a realizzare un'istruzione switch o case?
Viene spiegato più approfonditamente in perlsyn. 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 perlsyn ["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";
}
Come posso intercettare gli accessi a variabili, funzioni o metodi indefiniti?
Il metodo AUTOLOAD, discusso in "Autoloading" in perlsub e in "AUTOLOAD: Proxy Methods" in perltoot ["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);
Perché non si riesce a trovare un metodo incluso in questo stesso file?
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 perltoot 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 perlobj.
Assicuratevi di leggere perlmod a proposito della creazione dei moduli, e "Method invocation" in perlobj ["Invocazione di Metodi", NdT] a proposito dei pericoli degli oggetti indiretti.
Come posso sapere qual è il package corrente?
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";
}
Come posso commentare un vasto blocco di codice perl?
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 perlpod per maggiori dettagli.
Come si cancella un package?
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() .
Come posso usare una variabile come nome di una variabile?
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.
Cosa significa "bad interpreter" ["interprete non valido", NdT]?
(contributed by 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.
AUTORE E COPYRIGHT
Copyright (c) 1997-2005 Tom Christiansen, Nathan Torkington e altri autori menzionati. Tutti i diritti riservati.
La traduzione in italiano è a cura del progetto pod2it ( http://pod2it.sourceforge.net/ )
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.