开发者

Most efficient way of checking for a return from a function call in Perl

开发者 https://www.devze.com 2023-02-04 14:11 出处:网络
I want to add the return value from the function call to an array iff something is returned (not by default, i.e. if I have a return state开发者_运维问答ment in the subroutine.)

I want to add the return value from the function call to an array iff something is returned (not by default, i.e. if I have a return state开发者_运维问答ment in the subroutine.)

so I'm using unshift @{$errors}, "HashValidator::$vfunction($hashref)"; but this actually adds the string of the function call to the array. I also tried unshift @{$errors}, $temp if defined my $temp = "HashValidator::$vfunction($hashref)"; with the same result. What would a perl one-liner look like that does this efficiently (I know I can do the ugly, multi-line check but I want to learn).

Thanks,


iff something is returned (not by default, i.e. if I have a return statement in the subroutine.)

There could be a gotcha here. Perl always returns something, even if you don't mean to:

my $failures = 0;
sub word_to_number {
    my $_ = shift;
    /one/ and return 1;
    /two/ and return 2;
    ++$failures; # whoops, equivalent to return ++$failures
}

The last expression in a sub is used as the return value if there is no explicit return. To return "nothing", use bare return, which returns undef or the empty list, depending on context:

my $failures = 0;
sub word_to_number {
    my $_ = shift;
    /one/ and return 1;
    /two/ and return 2;
    ++$failures;
    return;
}

This behaviour is actually useful for things like sorting:

my @results = sort { $a->name cmp $b->name } @list;

where we have passed in the anonymous subroutine:

{
    $a->name cmp $b->name # equivalent to return $a->name cmp $b->name
}


In this case, there is no need to use the string form of eval (or any form for that matter). Not only is it slow, but it also can silently trap errors and could lead to untrusted code execution if used with a tainted input. To write a virtual function call in Perl, you can either work with the symbol table directly, or use a symbolic reference:

use 5.010;
use warnings;
use strict;

{package HashValidator;
    sub test_ok   {exists $_[0]{ok}}
    sub test_fail {exists $_[0]{fail}}
}


my $hashref = {ok => 1};
my $errors;

for my $vFunction qw(test_ok test_fail) {

    # to call the function:
    say "glob deref: $vFunction: ", $HashValidator::{$vFunction}->($hashref);
    {no strict 'refs';
    say "symbolic:   $vFunction: ", &{"HashValidator::$vFunction"}($hashref)}

    # to conditionally use the result (if it is a true boolean value):
    if (my $ret = $HashValidator::{$vFunction}->($hashref)) {
        push @$errors, $ret;
    }

    # or to keep the function call in list context:
    push @$errors, grep $_, $HashValidator::{$vFunction}->($hashref);

    # or to golf it:
    push @$errors, $HashValidator::{$vFunction}->($hashref) || ();
}

say @$errors.': ', join ', ' => @$errors;

which prints:

glob deref: test_ok: 1
symbolic:   test_ok: 1
glob deref: test_fail: 
symbolic:   test_fail: 
3: 1, 1, 1

If you are working with object oriented code, virtual method calls are even easier, with no symbol table or symbolic references:

$obj->$vMethod(...)


try using eval:

push @{$errors}, eval "HashValidator::$vfunction($hashref)"

The following works for me with perl 5.12, and checks for undef return values:

my $foo = "foo";
my $val = eval "Foo::$foo()"
push @arry,$val if ($val);
0

精彩评论

暂无评论...
验证码 换一张
取 消