2014-12-14 4 views
3

Я довольно новичок в Perl и с трудом переворачиваю голову вокруг своей неявной системы типов. То, что мне нравится, это простая функция-обертка, которая имеет ту же подпись, что и функция, которую она обертывает, чтобы ее можно было использовать на своем месте.Правильное прямое возвращаемое значение в Perl

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

sub wrapper { 
    my ($first) = @_; 
    print "before. first argument: $first\n"; 
    return orig(@_); 
} 

Однако, если я хочу, чтобы выполнить код в обертке после выполнения orig Я не знаю, как я сохраняю типы. По моему мнению, так же, как и вход в perl-функцию, всегда является массивом скаляров, поэтому выход. Таким образом, решение должно быть таким:

sub wrapper { 
    my ($first) = @_; 
    print "before. first argument: $first\n"; 
    my @result = orig(@_); 
    print "after"; 
    return @result; 
} 

Но это не работает должным образом. Что мне не хватает? Как написать такую ​​функцию обертки, что она работает правильно для произвольных типов возврата?

ответ

7

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

Нет, не совсем.

Функции Perl можно вызывать в контексте списка, скалярном контексте или пустоте контекста.

some_function(@args);     # void 
my $result = some_function(@args); # scalar 
my @results = some_function(@args); # list 

Многие из встроенных функций Perl действуют по-разному в соответствии с контекстом, который они называют. Например, grep возвращает список результатов в контексте списка и количество результатов в скалярном контексте.

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

Даже если вы сознательно не хочет, чтобы написать функцию, которая ведет себя по-разному в зависимости от контекста, вы можете в конечном итоге сделать это случайно, возвращая контекстную выражение, как grep или map, или массив (массивы в скаляр контекст возвращает их длину).

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

sub wrapper { 
    my ($first) = @_; 
    print "before. first argument: $first\n"; 
    my @result = 
     wantarray   ? orig(@_) : 
     defined wantarray ? scalar orig(@_) : 
     do { orig(@_);() }; 
    print "after"; 
    wantarray ? @result : $result[0]; 
} 

Теперь, если Ваша обертка не нужно изменять @_, и не нужно изменять возвращаемые значения, то Class::Method::Modifiers может сделать это немного проще:

use Class::Method::Modifiers; 

sub wrapper { orig(@_) } # do nothing in the wrapper itself 

before wrapper => sub { 
    my ($first) = @_; 
    print "before. first argument: $first\n"; 
}; 

after wrapper => sub { 
    print "after"; 
}; 
+0

Да, это было. Выполнение диспетчеризации Wantarray заставляет его работать. Тем не менее, я попробую подход с Class :: Method :: Modifiers, так как это было бы самым элегантным. –

1

Я подозреваю, что есть лучший способ сделать то, что вы хотите в Perl, но так как вы указали только механизм Я могу рассказать вам только, как заставить его работать.

Возвращаемое значение подпрограммы Perl зависит от контекста вызова.Внутри подпрограммы вы можете использовать встроенный оператор wantarray для определения контекста - он вернет значение true, если вызов был в контексте списка, и false в противном случае.

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

sub wrapper { 
    my ($first) = @_; 

    print "before. first argument: $first\n"; 

    if (wantarray) { 
     my @result = orig(@_); 
     print "after"; 
     return @result; 
    } 
    else { 
     my $result = orig(@_); 
     print "after"; 
     return $result; 
    } 
} 

Но имейте в виду, что данная подпрограмма, вероятно, означало быть вызван либо скаляр или контекст списка. Написание чего-то, что ведет себя по-другому в соответствии с контекстом, редко, поэтому вы обычно хотите только одну из этих двух условных ветвей в зависимости от поведения orig.

+0

Это решение не различает '! Wantarray' и '! определенный wantarray'. Некоторые субтитры действительно возвращают разные значения в зависимости от них, поэтому эта оболочка не является полностью прозрачной. –