web-dev-qa-db-fra.com

Comment puis-je appeler avec élégance un sous-programme Perl dont le nom est contenu dans une variable?

Je conserve le nom du sous-programme que je souhaite appeler au moment de l'exécution dans une variable appelée $ action. Ensuite, je l’utilise pour appeler ce sous-marin au bon moment:

&{\&{$action}}();

Fonctionne bien. La seule chose que je n'aime pas, c'est que c'est moche et chaque fois que je le fais, je me sens obligé d'ajouter un commentaire pour le prochain développeur:

# call the sub by the name of $action

Quelqu'un connaît une façon plus jolie de le faire?


MISE À JOUR: L'idée ici était d'éviter de devoir maintenir une table de répartition chaque fois que j'ajoutais un nouveau sous-appelant, étant donné que je suis le seul développeur, je ne m'inquiète pas de ce que les autres programmeurs respectent ou ne respectent pas les règles. Sacrifier un peu de sécurité pour ma commodité. Au lieu de cela, mon module de dispatch vérifie $ action pour s'assurer que 1) c'est le nom d'un sous-programme défini et non pas du code malveillant à exécuter avec eval, et 2) qu'il ne lancerait aucun sous-répertoire précédé d'un trait de soulignement, ce qui serait: marqués comme internes uniquement par cette convention de nommage.

Des idées sur cette approche? J'oublierai tout le temps les sous-routines en liste blanche dans la table de répartition, et mes clients préfèrent que je me trompe du côté de "ça marche" que de "c'est méchant en sécurité". (temps très limité pour développer des applications)


FINAL UPDATE: Je pense avoir choisi une table de répartition après tout. Bien que je sois curieux de savoir si quelqu'un qui lit cette question a déjà essayé d'en éliminer une et comment il l'a fait, je dois m'incliner devant la sagesse collective. Merci à tous, beaucoup de bonnes réponses.

53
Marcus

Plutôt que de stocker les noms de sous-routines dans une variable et de les appeler, une meilleure méthode consiste à utiliser un hachage de références de sous-routines (autrement appelé table dispatch .)

my %actions = ( foo => \&foo,
                bar => \&bar,
                baz => sub { print 'baz!' } 
                ... 
              );

Ensuite, vous pouvez appeler le bon facilement:

$actions{$action}->();

Vous pouvez également ajouter des vérifications pour vous assurer que $action est une clé valide dans le hachage, etc.

En général, évitez les références symboliques (ce que vous êtes en train de faire) car elles causent toutes sortes de problèmes. De plus, l’utilisation de véritables références de sous-programmes fonctionnera avec strict activé.

79
friedo

Juste &$action(), mais il est généralement préférable d'utiliser coderefs depuis le début ou d'utiliser un hachage de répartiteur. Par exemple:

my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();
21
Harmen

Hein? Tu peux juste dire

    $action->()

Exemple:

    sub f { return 11 }
    $action = 'f';
    print $action->();


    $ Perl subfromscalar.pl
    11

Des constructions comme

    'f'->()     # equivalent to   &f()

aussi travailler.

16
mob

Je ne suis pas sûr de comprendre ce que vous voulez dire. (Je pense que c'est un autre groupe récent de questions "Comment utiliser une variable comme nom de variable?", Mais peut-être pas.) 

Dans tous les cas, vous devriez pouvoir affecter un sous-programme entier à une variable (en tant que référence), puis l'appeler directement:

# create the $action variable - a reference to the subroutine
my $action = \&preach_it;
# later - perhaps much later - I call it
$action->();

sub preach_it {
    print "Can I get an amen!\n"
}
11
Telemachus

La chose la plus importante est: pourquoi voulez-vous utiliser la variable comme nom de fonction? Que se passera-t-il si ce sera 'eval'? Y a-t-il une liste de fonctions pouvant être utilisées? Ou peut-il être n'importe quelle fonction? Si la liste existe - quelle est sa durée?

Généralement, le meilleur moyen de gérer de tels cas est d'utiliser des tables de répartition:

my %dispatch = (
   'addition' => \&some_addition_function,
   'multiplication' => sub { $self->call_method( @_ ) },
);

Et puis juste:

$dispatch{ $your_variable }->( 'any', 'args' );
10
user80168
__PACKAGE__->can($action)->(@args);

Pour plus d'informations sur can (): http://perldoc.Perl.org/UNIVERSAL.html

6
Nehal J Wani

Je fais quelque chose de similaire. Je l'ai divisé en deux lignes pour le rendre légèrement plus identifiable, mais ce n'est pas beaucoup plus joli.

my $sub = \&{$action};
$sub->();

Je ne connais pas de manière plus correcte ou plus jolie de le faire. Pour ce que cela vaut, nous avons un code de production qui fait ce que vous faites, et cela fonctionne sans avoir à désactiver use strict.

6
Wes

Chaque paquet en Perl est déjà une table de hachage. Vous pouvez ajouter des éléments et les référencer par les opérations de hachage normales. En général, il n'est pas nécessaire de dupliquer la fonctionnalité par une table de hachage supplémentaire.

#! /usr/bin/Perl -T
use strict;
use warnings;

my $tag = 'HTML';

*::->{$tag} = sub { print '<html>', @_, '</html>', "\n" };

HTML("body1");

*::->{$tag}("body2");

Le code imprime:

 <html> body1 </ html> 
 <html> body2 </ html> 

Si vous avez besoin d'un espace de nom séparé, vous pouvez définir un package dédié.

Voir perlmod pour plus d'informations.

3
ceving

Je l'ai fait de cette façon:

@func = qw(cpu mem net disk);
foreach my $item (@func){
    $ret .= &$item(1);
}
1
Putnik

Soit utiliser 

&{\&{$action}}();

Ou utilisez eval pour exécuter la fonction:

eval("$action()");
1
Grolim

J'ai utilisé ceci: cela fonctionne pour moi.

(\$action)->();

Ou vous pouvez utiliser 'do', assez similaire aux posts précédents:

$p = do { \&$conn;}; 
$p->();
0
Yang

Si ce n'est que dans un programme, écrivez une fonction qui appelle un sous-programme en utilisant un nom de variable et que vous ne devez documenter/excuser qu'une seule fois

0
Dean J