代码之家  ›  专栏  ›  技术社区  ›  Will

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

  •  3
  • Will  · 技术社区  · 15 年前

    我试图写一个接受coderef参数的sub。我的sub执行一些初始化,调用coderef,然后执行一些清理。

    我需要使用调用sub的相同上下文(标量、列表、空上下文)调用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 回复  |  直到 14 年前
        1
  •  1
  •   user502515    15 年前

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

    第二,你可以把清理的东西移到它自己的函数中。我想起了这样的事:

    sub perform
    {
        my($self, $code) = @_;
        if (!defined(wantarray)) {
                $code->();
                return;
        }
        return wantarray ? &cleanup($code->()) : &cleanup(scalar($code->()));
    }
    
        2
  •  3
  •   cjm    15 年前

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

        3
  •  1
  •   cjm    15 年前

    我想我会这样做:

    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 返回。