2010-04-27 1 views
6

Я пытаюсь написать некоторый абстрактный код для поиска по списку похожих объектов для первого, чьи атрибуты соответствуют определенным значениям. Чтобы сделать это, мне нужно вызвать кучу методов доступа и проверить все их значения один за другим. Я хотел бы использовать абстракцию, как это:В Perl, как я могу вызвать метод, имя которого у меня есть в строке?

sub verify_attribute { 
    my ($object, $attribute_method, $wanted_value) = @_; 
    if (call_method($object, $attribute_method) ~~ $wanted_value) { 
     return 1; 
    } 
    else { 
     return; 
    } 
} 

Тогда я могу петлю через хэш, ключи которого являются аксессорами именами методов и значение которых является значением Я ищу для этих атрибутов. Например, если хэш называется %wanted, я мог бы использовать такой код, чтобы найти объект, я хочу:

my $found_object; 
FINDOBJ: foreach my $obj (@list_of_objects) { 
    foreach my $accessor (keys %wanted) { 
     next FINDOBJ unless verify_attribute($obj, $accessor, $wanted{$accessor}); 
    } 
    # All attrs verified 
    $found_object = $obj; 
    last FINDOBJ; 
} 

Конечно, единственная проблема заключается в том, что call_method не exsit. Или это? Как я могу вызвать метод, если у меня есть строка, содержащая его имя? Или есть лучшее решение этой проблемы?

ответ

5
my $found_object; 
FINDOBJ: foreach my $obj (@list_of_objects) { 
    foreach my $accessor (keys %wanted) { 
    next FINDOBJ unless $obj->$accessor() == $wanted{$accessor}; 
    } 
    # All attrs verified 
    $found_object = $obj; 
    last; 
} 

Да, вы можете назвать методы таким образом. Ни одна строка (или любая другая) не была задействована. Кроме того, заменить == с eq или =~ в зависимости от типа данных ...

Или, для некоторых дополнительных кредитов, сделать это функциональный путь: (все() должно быть действительно часть списка :: Util!)

use List::Util 'first'; 

sub all (&@) { 
    my $code = shift; 
    $code->($_) || return 0 for @_; 
    return 1; 
} 

my $match = first { 
        my $obj = $_; 
        all { $obj->$_ == $attrs{$_} } 
         keys %wanted 
        } @list_of_objects; 

Обновление: По общему признанию, первое решение является менее обфускационным, поэтому предпочтительнее. Но поскольку кто-то отвечает на вопросы, вы добавляете немного сахара, чтобы сделать его интересным для себя! ;-)

+0

Ye ах, я просто подумал об этом несколько секунд назад по проб и ошибок. Круто. –

+1

Я просто использую 'List :: AllUtils'. Или 'Util :: Any qw (: all)'. Но в моем примере кода я стараюсь придерживаться сути. –

+0

Я считаю, что для этого вам нужно отключить проверки строгости с помощью 'no strict'' (не обязательно глобально) - правильно? –

0

Функциональный способ это круто, но для чайников, как я Eval правила:

test.pl

#!/usr/bin/perl -l 
use F; 
my $f = F->new(); 

my $fun = 'lol'; # method of F 

eval '$f->'.$fun.'() '; # call method of F, which name is in $fun var 

F.pm

package F; 

sub new 
{ 
    bless {}; 
} 


sub lol 
{ 
    print "LoL"; 
} 
1; 

[корень @ ALT-24 корень] # perl test.pl

LoL