Как передать параметры подпрограммам Perl, определенным с помощью eval?

Я использую файл конфигурации (в YAML) для определения типов, которые используются позже для проверки других значений конфигурации, необходимых для моего приложения:

---
action: >
        use List::MoreUtils;
        my $value = $_;
        any { $value eq $_ } qw(fatal keep merge non-fatal replace);
dir   : return defined $_ ? -d $_ : -1;
file  : return defined $_ ? -f $_ : -1;
string: 1;


---
config-element:
    value: foo
    type : file
etc ...

Идея состоит в том, чтобы eval определить каждое определение типа, поместить их в хэш, а затем вызвать для проверки данных конфигурации (следующее схематично для простоты понимания):

#throw sub refs into hash
my %type_sub;
foreach my $key (keys %$type_def_ref) {
    my $sub_str = "sub {$type_def_ref->{$key}}";
    $type_sub{$key} = eval $sub_str;

}

#validate (myfile is a real file in the cwd)
print $type_sub{file}->('myfile'),"\n";
print $type_sub{action}->('fatal'), "\n";

Проблема в том, что подпрограммы в %type_sub не принимают параметры. В приведенном выше случае первый оператор печати выводит -1, а второй выводит:

Use of uninitialized value $value in string eq at (eval 15) line 1.
Use of uninitialized value $_ in string eq at (eval 15) line 1.
Can't call method "any" without a package or object reference at 
(eval 15) line 1.

это совсем не то, что я ожидаю, но подпрограммы вызываются.

Что я делаю не так?

РЕДАКТИРОВАТЬ: я был небрежен, и теперь все работает нормально. Спасибо Фридо.


person gvkv    schedule 19.01.2010    source источник


Ответы (2)


Параметры вашей подпрограммы будут в массиве @_, а не $_. Чтобы получить первый параметр, загляните в $_[0] или выполните my $foo = shift;. (shift по умолчанию работает с @_.)

Что касается any, я считаю, что проблема связана с тем, что any не может загрузить свой прототип во время выполнения (прототипы подпрограмм можно вызывать только во время компиляции). Возможно, вам потребуется использовать явные скобки и явную ссылку на подпрограмму:

any( sub { $value eq $_ }, qw(fatal keep merge non-fatal replace) );
person friedo    schedule 19.01.2010
comment
Ух ты. Я всегда использовал @_ со сдвигом или назначением списка, а $_ настолько распространен, что я совершенно забыл, что он не устанавливается автоматически. - person gvkv; 19.01.2010
comment
Я только что попробовал ваше решение для «любого», но не повезло. Здесь более общий вопрос о том, как определить подпрограмму со строкой, используя eval или какой-либо другой метод. Возможно, я спрошу об этом позже. - person gvkv; 19.01.2010
comment
Я починил это. List::MoreUtils по умолчанию ничего не экспортирует. - person gvkv; 19.01.2010

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

Я подробно рассказываю об этом в главе «Конфигурация» книги Mastering Perl, а также в главах, посвященных динамическому подпрограммы.

Код не принадлежит конфигурации. Говорите так, пока не поверите.

person brian d foy    schedule 19.01.2010
comment
Вы, конечно, правы, но я решил, что остроты не имеют большого значения (и я все равно нарушил этот принцип). - person gvkv; 21.01.2010
comment
Другая проблема заключается в том, что я не знаю, как это сделать, не отключая строгие ссылки. - person gvkv; 21.01.2010