将标量/列表上下文传递给被调用的子例程

时间:2010-11-28 00:07:38

标签: perl

我正在尝试编写一个带有coderef参数的子程序。我的子做了一些初始化,调用了coderef,然后进行了一些清理。

我需要使用我的sub调用的相同上下文(标量,列表,void上下文)来调用coderef。我能想到的唯一方法就是这样:

sub perform {
    my ($self, $code) = @_;

    # do some initialization...

    my @ret;
    my $ret;

    if (not defined wantarray) {
        $code->();
    } elsif (wantarray) {
        @ret = $code->();
    } else {
        $ret = $code->();
    }

    # do some cleanup...

    if (not defined wantarray) {
        return;
    } elsif (wantarray) {
        return @ret;
    } else {
        return $ret;
    }
}

显然,此代码中存在大量冗余。有没有办法减少或消除任何这种冗余?

编辑我后来意识到我需要在$code->()块中运行eval,以便即使代码消失也能运行清理。添加eval支持,并结合user502515和cjm的建议,这就是我的想法。

sub perform {
    my ($self, $code) = @_;

    # do some initialization...

    my $w = wantarray;
    return sub {
        my $error = $@;

        # do some cleanup...

        die $error if $error;   # propagate exception
        return $w ? @_ : $_[0];
    }->(eval { $w ? $code->() : scalar($code->()) });
}

这消除了冗余,但不幸的是现在控制流程有点难以理解。

3 个答案:

答案 0 :(得分:3)

查看CPAN上的Contextual::Return模块。我认为它可以让你做你想做的事情(可能还有更多)。

答案 1 :(得分:1)

您可以提前排除!defined wantarray个案,因为没有清理工作(因为$code->()的结果(如果有的话)没有存储)。这将从剩余的功能中删除一个案例,使其更简单。

其次,您可以将清理内容移动到自己的功能中。我想到了这样的事情:

sub perform
{
    my($self, $code) = @_;
    if (!defined(wantarray)) {
            $code->();
            return;
    }
    return wantarray ? &cleanup($code->()) : &cleanup(scalar($code->()));
}

答案 2 :(得分:1)

我想我会这样做:

sub perform {
    my ($self, $code) = @_;

    # do some initialization...

    my @ret;
    if (not defined wantarray) {
        $code->();
    } else {
        @ret = wantarray ? $code->() : scalar $code->();
    }

    # do some cleanup...

    return wantarray ? @ret : $ret[0];
}

你仍然有两个wantarray项检查,但是你的cleanup函数需要一个才能正确返回它传入的值。你不需要担心关于第二次检查中的undef个案,因为在这种情况下,perform返回的内容并不重要。