36 votes

Quel est le rôle du bloc BEGIN en Perl ?

Je sais que le bloc BEGIN est compilé et exécuté avant le corps principal d'un programme Perl. Si vous n'êtes pas sûr de cela, essayez simplement d'exécuter la commande perl -cw sur ceci :

#!/ms/dist/perl5/bin/perl5.8

use strict;
use warnings;

BEGIN {
    print "Hello from the BEGIN block\n";
}

END {
    print "Hello from the END block\n";
}

On m'a appris que la compilation et l'exécution précoces d'un bloc BEGIN permettent au programmeur de s'assurer que toutes les ressources nécessaires sont disponibles avant l'exécution du programme principal.

J'ai donc utilisé des blocs BEGIN pour m'assurer que des éléments tels que les connexions à la base de données ont été établis et sont disponibles pour être utilisés par le programme principal. De même, j'utilise des blocs END pour m'assurer que toutes les ressources sont fermées, supprimées, terminées, etc. avant que le programme ne se termine.

Après une discussion ce matin, je me demande si ce n'est pas la mauvaise façon de considérer les blocs BEGIN et END.

Quel est le rôle prévu d'un bloc BEGIN en Perl ?

Mise à jour 1 : Je viens de découvrir pourquoi la connexion DBI n'a pas fonctionné. Après avoir reçu ce petit programme Perl :

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";

lorsqu'il est exécuté, il imprime 12.

Mise à jour 2 : Grâce au commentaire d'Eric Strom ci-dessous, cette nouvelle version est plus claire :

use strict;
use warnings;

my $x = 12;
my $y;

BEGIN {
    $x = 14;
    print "x => $x\n";
    $y = 16;
    print "y => $y\n";
}

print "x => $x\n";
print "y => $y\n";

et la sortie est

x => 14
y => 16
x => 12
y => 16

Encore une fois, merci Eric !

31voto

Eric Strom Points 30894

Alors que BEGIN et END peuvent être utilisés comme vous le décrivez, l'usage typique étant d'apporter des changements qui affectent la compilation suivante.

Par exemple, le use Module qw/a b c/; signifie réellement :

BEGIN {
   require Module;
   Module->import(qw/a b c/);
}

De même, la déclaration de sous-routine sub name {...} est en fait :

BEGIN {
   *name = sub {...};
}

Comme ces blocs sont exécutés au moment de la compilation, toutes les lignes qui sont compilées après l'exécution d'un bloc utiliseront les nouvelles définitions faites par les blocs BEGIN. C'est ainsi que vous pouvez appeler des sous-routines sans parenthèses, ou que divers modules "changent la façon dont le monde fonctionne".

END peuvent être utilisés pour nettoyer les changements que l'option BEGIN mais il est plus courant d'utiliser des objets avec une DESTROY méthode.

Si l'état que vous essayez de nettoyer est une connexion DBI, faire cela dans une END Le bloc est bien. Je ne créerais pas la connexion dans un bloc BEGIN bloquer cependant pour plusieurs raisons. En général, il n'est pas nécessaire que la connexion soit disponible au moment de la compilation. L'exécution d'actions telles que la connexion à une base de données au moment de la compilation ralentira considérablement tout éditeur que vous utilisez et qui dispose d'un contrôle syntaxique (car il exécute perl -c ).

22voto

tchrist Points 47116

Avez-vous essayé de remplacer le BEGIN{} pour un INIT{} bloc ? C'est l'approche standard pour des choses comme modperl qui utilisent le modèle "compile-once, run-many", car vous devez initialiser les choses à nouveau à chaque exécution, et pas seulement une fois pendant la compilation.

Mais je dois demander pourquoi tout ça est dans un bloc spécial de toute façon. Pourquoi ne pas simplement faire une sorte de prepare_db_connection() puis l'appeler comme il faut au démarrage du programme ?

Quelque chose qui ne fonctionnera pas dans un BEGIN{} aura également le même problème si c'est le code principal d'un fichier de module qui est obtenu use d. C'est une autre raison possible d'utiliser un INIT{} bloc.

J'ai aussi vu des problèmes de récursion mutuelle mortelle qui doivent être résolus en utilisant quelque chose comme un require au lieu de use ou un INIT{} au lieu d'un BEGIN{} . Mais c'est plutôt rare.

Considérez ce programme :

% cat sto-INIT-eg
#!/usr/bin/perl -l
print               "    PRINT: main running";
die                 "    DIE:   main dying\n";
die                 "DIE XXX /* NOTREACHED */";
END         { print "1st END:   done running"    }
CHECK       { print "1st CHECK: done compiling"  }
INIT        { print "1st INIT:  started running" }
END         { print "2nd END:   done running"    }
BEGIN       { print "1st BEGIN: still compiling" }
INIT        { print "2nd INIT:  started running" }
BEGIN       { print "2nd BEGIN: still compiling" }
CHECK       { print "2nd CHECK: done compiling"  }
END         { print "3rd END:   done running"    }

Lorsqu'il est compilé uniquement, il produit :

% perl -c sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
sto-INIT-eg syntax OK

Alors que lors de la compilation et exécuté, il produit ceci :

% perl sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
1st INIT:  started running
2nd INIT:  started running
    PRINT: main running
    DIE:   main dying
3rd END:   done running
2nd END:   done running
1st END:   done running

Et l'interpréteur de commandes signale une sortie de 255, conformément à la norme die .

Vous devez pouvoir vous arranger pour que la connexion se fasse quand vous en avez besoin, même si une BEGIN{} s'avère trop précoce.

Hm, je viens de me souvenir. Il n'y a aucune chance que tu fasses quelque chose avec DATA dans un BEGIN{} existe-t-il ? Ce n'est pas mis en place avant l'exécution de l'interpréteur ; ce n'est pas ouvert au compilateur.

5voto

oscfri Points 21

Bien que les autres réponses soient vraies, je trouve qu'il est également utile de mentionner l'utilisation de BEGIN et END lors de l'utilisation du -n ou -p passe à Perl.

De http://perldoc.perl.org/perlmod.html

Lorsque vous utilisez les commutateurs -n et -p en Perl, BEGIN et END fonctionnent exactement comme en awk, comme un cas dégénéré.

Pour ceux qui ne sont pas familiers avec le -n il indique à Perl d'envelopper le programme avec :

while (<>) {
    ...  # your program goes here
}

http://perldoc.perl.org/perlrun.html#Command-Switches si vous êtes intéressé par des informations plus spécifiques sur les commutateurs Perl.

À titre d'exemple pour démontrer l'utilisation de BEGIN avec le -n ce one-liner Perl énumère les lignes de la commande ls commandement :

ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;'

Dans ce cas, le BEGIN -block est utilisé pour initier la variable $i en le mettant à 1 avant de traiter les lignes de ls . Dans cet exemple, le résultat sera le suivant :

1: foo.txt
2: bar.txt
3: program.pl
4: config.xml

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X