Использование переменной в качестве имени метода в Perl - PullRequest
3 голосов
/ 26 января 2011

У меня есть Perl-скрипт (упрощенный), например так:

my $dh = Stats::Datahandler->new(); ### homebrew module

my %url_map = (
    '/(article|blog)/' => \$dh->articleDataHandler,
    '/video/' => \$dh->nullDataHandler,
); 

По сути, я собираюсь перебрать %url_map, и если текущий URL-адрес соответствует ключу, я хочу вызватьФункция, на которую указывает значение этой клавиши:

foreach my $key (keys %url_map) {
    if ($url =~ m{$key}) {
        $url_map{$key}($url, $visits, $idsite);
        $mapped = 1;
        last;
    }
}

Но я получаю сообщение:

Can't use string ("/article/") as a subroutine ref while "strict refs" in use at ./test.pl line 236.

Строка 236 оказывается строкой $url_map{$key}($url, $visits, $idsite);.

В прошлом я делал подобные вещи, но обычно я делаю это без параметров функции и без использования модуля.

Ответы [ 3 ]

5 голосов
/ 26 января 2011

Поскольку на этот вопрос здесь отвечают, несмотря на то, что он дурак, я также могу опубликовать правильный ответ:

Что вам нужно сделать, это сохранить ссылку на код в качестве значения в вашем хэше. Чтобы получить ссылку на код метода, вы можете использовать метод UNIVERSAL::can для всех объектов. Однако этого недостаточно, так как метод должен быть передан инвоканту. Поэтому лучше всего пропустить ->can и просто написать это так:

my %url_map = (
    '/(article|blog)/' => sub {$dh->articleDataHandler(@_)},
    '/video/'          => sub {$dh->nullDataHandler(@_)},
); 

Этот метод будет хранить ссылки на код в хэше, которые при вызове с аргументами будут вызывать соответствующие методы с этими аргументами.

В этом ответе пропущено важное соображение, и это означает, что caller правильно работает в методах. Если вам это нужно, см. Вопрос, на который я ссылался выше:

Как получить код ссылки на конструктор?

2 голосов
/ 26 января 2011

Вы переосмысливаете проблему.Выясните строку между двумя косыми чертами, затем найдите имя метода (не ссылка) в хэше.Вы можете использовать скалярную переменную в качестве имени метода в Perl;значение становится методом, который вы на самом деле вызываете:

 %url_map = (
      'foo' => 'foo_method',
      );

 my( $type ) = $url =~ m|\A/(.*?)/|;
 my $method = $url_map{$type} or die '...';
 $dh->$method( @args );

Постарайтесь избавиться от любых циклов, в которых большинство итераций для вас бесполезны.:)


мой предыдущий ответ, который мне не нравится, хотя он ближе к проблеме

Вы можете получить ссылку на метод вконкретный объект с can (если вы сами не реализовали его, чтобы сделать иначе):

my $dh = Stats::Datahandler->new(); ### homebrew module

my %url_map = (
   '/(article|blog)/' => $dh->can( 'articleDataHandler' ),
   '/video/'          => $dh->can( 'nullDataHandler' ),
);

Способ, которым вы вызываете метод и получаете ссылку на результат.Это не то, что вы хотите для отложенного действия.

Теперь, когда у вас есть это, вы вызываете это как обычную разыменование подпрограммы, а не вызов метода.Он уже знает свой объект:

BEGIN {
package Foo;

sub new { bless {}, $_[0] }
sub cat { print "cat is $_[0]!\n"; }
sub dog { print "dog is $_[0]!\n"; }
}

my $foo = Foo->new;

my %hash = (
    'cat' => $foo->can( 'cat' ),
    'dog' => $foo->can( 'dog' ),
    );

my @tries = qw( cat dog catbird dogberg dogberry );

foreach my $try ( @tries ) {
    print "Trying $try\n";
    foreach my $key ( keys %hash ) {
    print "\tTrying $key\n";
        if ($try =~ m{$key}) {
            $hash{$key}->($try);
            last;
            }
        }
    }
0 голосов
/ 26 января 2011

Лучший способ справиться с этим - заключить вызовы методов в анонимную подпрограмму, которую вы можете вызвать позже.Вы также можете использовать оператор qr для хранения правильных регулярных выражений, чтобы избежать неловкости интерполяции шаблонов в вещи.Например,

my @url_map = ( 
    { regex    => qr{/(article|blog)/},
      method   => sub { $dh->articleDataHandler }
    },
    { regex    => qr{/video/},
      method   => sub { $dh->nullDataHandler }
    }
);

Затем выполните его так:

foreach my $map( @url_map ) { 
    if ( $url =~ $map->{regex} ) { 
        $map->{method}->();
        $mapped = 1;
        last;
    }
}

Этот подход использует массив хешей, а не плоский хеш, поэтому каждое регулярное выражение может быть связано с анонимнымSub Ref, который содержит код для выполнения.Синтаксис ->() разыменовывает sub ref и вызывает его.Вы также можете передать параметры в подчиненную ссылку, и они будут видны в @_ внутри блока подчиненной.Вы можете использовать это для вызова метода с параметрами, если хотите.

...