【问题标题】:How can I create a Perl subroutine that accepts more than one block?如何创建一个接受多个块的 Perl 子例程?
【发布时间】:2015-01-18 15:10:36
【问题描述】:

使用原型,您可以创建一个接受代码块作为其第一个参数的子例程:

sub example (&) {
   my $code_ref = shift;
   $code_ref->();
}
example { print "Hello\n" };

我怎样才能做同样的事情,但不止一个代码块?我想使用代码块,而不是变量或sub { ... }

这不起作用:

sub example2 (&&) {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 { print "One\n" } { print "Hello\n" };

它给出了这个错误:

Not enough arguments for main::example2

【问题讨论】:

  • 你所说的“代码块”就是sub { ... },只是你不用sub关键字
  • @TLP:你的意思是这样吗? example2 sub { ... }, sub { ...}; 我意识到,我只想不使用 sub 关键字或变量。我猜这是做不到的。
  • 这也是我的猜测。原型不容易使用,一般建议是:不要使用它们。我会冒险猜测它们没有得到很好的维护和/或开发,因为大多数 Perl 程序员认为它们是死胡同并且不使用它们。
  • 就原型而言,(&) 和 (&@) 是无害的。
  • @Flimm:我们中的许多人都问过为什么,并且向您提供了几种可能有效也可能无效的解决方案。你不认为你欠我们一个为什么的答案吗?

标签: function perl parameter-passing subroutine


【解决方案1】:

我希望您意识到这只是代码调整,而您所获得的只是更简洁的语法,而牺牲了清晰度?

Perl 不允许您将多个裸块传递给子例程,但第二个实际参数可以是对子例程的调用,该子例程也采用单个块并简单地返回代码引用。

这个程序演示。请注意,我选择了pleasealso 作为子例程的名称。但是您必须使用既适合您自己的代码功能又不太可能与即将推出的核心语言扩展发生冲突的东西。

use strict;
use warnings;

sub please(&$) {
  my ($code1, $code2) = @_;
  $code1->();
  $code2->();
}

sub also(&) {
  $_[0];
}

please { print "aaa\n" } also { print "bbb\n" };

输出

aaa
bbb

【讨论】:

  • 接受,因为答案是:“Perl 不允许你将多个裸块传递给子例程”。 also 的东西很聪明,但在这种情况下它的行为与 sub 完全相同,所以我不妨使用 sub
  • 但是为什么会这样呢?由于我不知道的原因,Perl 似乎将下一个块视为哈希引用。
  • 难道 Perl 中的 block 不是数据类型(相对于 sub)?考虑一下:$x = { print "foobar\n" } - 它执行代码并创建一个哈希引用1 => undef
【解决方案2】:

为我工作...

sub example2  {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 ( sub { print "One\n" }, sub { print "Hello\n" });

仅出于 TMTOWTDI 的目的,这里是一种以 OP 请求的方式工作的方法

首先,做一个源过滤器

package poop;

use Filter::Util::Call;

sub import {
    my ($type) = @_;
    my ($ref)  = [];
    filter_add( bless $ref );
}

sub filter {
    my ($self) = @_;
    my ($status);
if (( $status = filter_read() ) > 0) { 
        if(!/sub/ && /example2/) {
            s/\{/sub \{/g;
            }
        }

    $status;
}
1;

必须使用第二个过滤器

use poop;

sub example2  {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 ( { print "One\n" }, { print "Hello\n" });

ps。这太可怕了,没有人希望在生产中看到这个

【讨论】:

    【解决方案3】:

    你可以 eval

    use experimental 'signatures';
    use feature 'say'; 
    
    sub doublethecode ($block1, $block2) { eval { $block1; }; eval { $block2;} } ;
    doublethecode \&{ print "OH " }, \&{ say for qw/HAI YOU LOLCAT :-P/  }; 
    

    输出

    OH, HAI
    YOU
    LOLCAT
    :-P
    

    然后忽略\&就可以了。

    v5.20 开始,您可以同时使用签名和:prototype 属性。 signatures 特性提供了更“通用”的语法和极简的参数检查。也许有一天,未来的 perl 将拥有某种“内置”(可选的、高度灵活的)类型系统来配合它,但现在的原型并不是那样的。所以像:

    sub doublethecode :prototype(\&\&) ($cr1, $cr2) { $cr1 ; $cr2 ; }
    

    不是它看起来的那样。由于原型、signatures 和带有:prototype(&&) 的签名可能无法为您提供您所想得到的,这可能已经足够了:

    sub doublethecode { eval { shift };  eval{ shift } } ;
    doublethecode &{ print "perl" }, &{ print "6" }
    

    输出

    perl6
    

    为了确保 perl 不认为 {} 是哈希,& 是必要的。但实际上......在这里使用匿名子例程sub { } 有什么问题

    【讨论】:

    • 你认为sub doublethecode ($block1, $block2) 会做什么?
    • @borodin 哎呀我想我们正在同时编辑对不起...我认为 sub doublethecode ($block1, $block2) { ... } 只是分配 @_ 的一种新方法,将它与属性 :prototype(&&) 一起使用不会似乎让这个练习变得更好:-)
    • 请看documentation for the experimental pragma。它相当于no warnings 'experimental'; use feature 'signatures';。即使这样,您的代码也会因Unknown warnings category 'experimental'Feature "signatures" is not supported 之类的消息而死,除非您有前面的use 5.020。是的,要运行您的解决方案,您需要运行 非常最新 版本的 Perl,该版本仅在今年 5 月发布。有骑在最前沿,有炫耀所有关于新设施的警告。你知道你在哪里。
    • @Borodin,点得好——我很熟悉编译指示的作用,我应该指出来,我的错。我认为使用sub {} 是一个非常清晰且使用良好的perl 习惯用法。在越来越严肃的 ECMA/Javascript 世界中,匿名函数(更加冗长)风靡一时。在 perl 中,sub{...}, sub{...} 实际上是一个自我记录的功能,我们拥有它。 OP 可能应该解释他们为什么不使用它。
    • 我假设您的意思是“越来越严肃的 ECMAScript / JavaScript 世界”? JavaScript 现在拥有了几十年前 FORTRAN 和 C 之后的地位:缺点无穷无尽,但人气牌胜过一切。
    【解决方案4】:

    Fwiw,在 Perl 6 中:

    sub doublecode (&foo, &bar) { foo(), bar() }
    doublecode { print 1 }, { print 2 } # prints 12
    

    但请参阅my partial list of caveats about Perl 6

    【讨论】:

      【解决方案5】:

      这对我有用:

           example { print "One\n" } sub { print "Hello\n" };
      

      【讨论】:

      • 你试过了,看看它是否有效?它不适合我。
      • 我更新了我的答案......也许你可以告诉我们为什么传递块而不是匿名订阅很重要。是 Ruby,不是吗?
      • 问题不是关于 Ruby 而是关于 Perl(在标题和标签中指出)。
      • @LenJaffe:像这样的想法——尽管我承认,不是这个特别的想法——构成了编程语言概念的核心。 OP 有一个必须在左手完成的工作,以及一个可靠地遵循规则的实体在他们的右手。你应该为他们做什么?
      • 带他走,然后向犀牛投球。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      • 2021-12-12
      • 2017-08-10
      • 1970-01-01
      相关资源
      最近更新 更多