【问题标题】:Pass scalar/list context to called subroutine将标量/列表上下文传递给被调用的子例程
【发布时间】:2011-01-03 13:51:45
【问题描述】:

我正在尝试编写一个带有 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;
    }
}

显然,这段代码中存在大量冗余。有什么办法可以减少或消除这种冗余?

EDIT 后来我意识到我需要在eval 块中运行$code->(),以便即使代码死了也能运行清理。添加 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->()) });
}

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

【问题讨论】:

  • 我还应该指出,如果do some cleanup... 不是必需的,则整个函数可以简化为return $code->(),并且上下文将自动被调用的子例程继承。不幸的是,清理是必要的。
  • “执行”?这是马戏团的代码吗?

标签: perl


【解决方案1】:

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

【讨论】:

  • 我支持这个。尽管它是通常的奇异的 Damianized 界面,但它确实解决了这个问题,即使它有点矫枉过正。
  • 我看不到 Contextual::Return 除了另一个依赖项之外如何添加任何内容。这真的不是它试图解决的问题。它不能帮助您将正确的上下文传递给 coderef,这确实是这个问题的难点。而return wantarray ? @ret : $ret[0]; 几乎没有复杂到需要加载一个模块来“简化”它。
  • @cjm:感谢您的编辑...CPAN 页面何时会提供指向任何模块的通用链接?这是一个麻烦 - 我还没有记住在没有版本的情况下访问特定模块的“正确”方法,并且按照您的链接结束了我提供的链接(今天;我知道它可能随时更改)。如果有一个规范链接,如果该网站能够提供获取它的方法将会很有帮助!
  • 如果您查看 CPAN 页面的右上角(作者图片的正上方),您会看到“永久链接”一词。这就是你想要的链接。
【解决方案2】:

您可以提前排除!defined wantarray 的情况,因为没有清理工作要做(因为$code->() 的结果(如果有的话)没有被存储)。这从其余函数中删除了一个案例,使其更简单。

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

子执行 { 我的($self, $code) = @_; 如果(!定义(wantarray)){ $code->(); 返回; } 返回想要数组? &cleanup($code->()) : &cleanup(标量($code->())); }

【讨论】:

  • 不幸的是,清理工作需要始终进行,即使是从 void 上下文调用。但我喜欢您使用单独的cleanup 子例程自动为被调用的子例程提供上下文的建议。
【解决方案3】:

我想我会这样做:

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 函数需要一个才能正确返回它传入的值。您不必担心 @987654324 @case 在第二次检查中,因为在这种情况下,perform 返回的内容无关紧要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 2013-08-12
    • 1970-01-01
    • 2021-06-28
    • 2016-12-18
    • 2011-02-22
    • 1970-01-01
    相关资源
    最近更新 更多