如何创建一个接受代码块的Perl子例程

Iva*_*zar 18 perl arguments subroutine

我有一组子程序,如下所示:

sub foo_1($) {
  my $name = shift;
  my $f; 

  run_something();
  open($f, $name) or die ("Couldn't open $name");
  while (<$f>) {
    //Something for foo_1()
  }
  close($f); 
  do_something_else();

}
Run Code Online (Sandbox Code Playgroud)

我有四个或更多看起来相同,唯一改变的是while块的主体.我想抽象一下并停止复制粘贴代码.

  • 有没有办法编写一个接受代码块并执行它的子程序?

为了给出更多的上下文,不同的foo子程序是一个不同的有限状态机(FSM),它读取不同文件的内容并将数据提供给哈希引用.也许比我想要完成的事情更聪明.

Eri*_*rom 36

Perl提供了一个称为子例程原型的系统,允许您编写以类似于内置函数的方式解析的用户子.您要模拟的内置函数是map,grep或者sort每个都可以将块作为第一个参数.

要做到这一点与原型,可以使用sub name (&) {...}&告诉Perl的第一个参数的函数或者是块(有或无sub),或文字子程序\&mysub.该(&)原型指定一个且只有一个说法,如果你需要的代码块后传递多个参数,因为你可以把它写(&@)这意味着,一个代码块后面的列表.

sub higher_order_fn (&@) {
    my $code = \&{shift @_}; # ensure we have something like CODE

    for (@_) {
        $code->($_);
    }
}
Run Code Online (Sandbox Code Playgroud)

该子例程将在传入列表的每个元素上运行传入的块.该\&{shift @_}看起来有点神秘,但它的作用是关闭名单的第一要素,这应该是一个代码块的变化.将&{...}值取消引用作为子例程(调用任何重载),然后\立即获取对它的引用.如果值是CODE引用,则返回不变.如果它是一个重载的对象,它将变成代码.如果无法强制转换为CODE,则会引发错误.

要调用此子例程,您可以编写:

higher_order_fn {$_ * 2} 1, 2, 3;
# or
higher_order_fn(sub {$_ * 2}, 1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

(&@)允许您将参数写为map/ greplike块的原型仅在使用高阶函数作为函数时起作用.如果您将它用作方法,则应省略原型并以这种方式编写它:

sub higher_order_method {
    my $self = shift;
    my $code = \&{shift @_};
    ...
    $code->() for @_;
}
...
$obj->higher_order_method(sub {...}, 'some', 'more', 'args', 'here');
Run Code Online (Sandbox Code Playgroud)


dax*_*xim 12

sub bar {
   my ($coderef) = @_;
   ?
   $coderef->($f, @arguments);
   ?
}

bar(sub { my ($f) = @_; while … }, @other_arguments);
Run Code Online (Sandbox Code Playgroud)

或者与命名的coderef纠缠不清:

my $while_sub = sub {
    my ($f) = @_;
    while …
    ?
};
bar($while_sub, @other_arguments);
Run Code Online (Sandbox Code Playgroud)

编辑:高阶Perl书充满了这种编程.


ike*_*ami 9

你想要&原型.

sub foo(&@) {
    my ($callback) = shift;
    ...
    $callback->(...);
    ...
}
Run Code Online (Sandbox Code Playgroud)

品牌

foo { ... } ...;
Run Code Online (Sandbox Code Playgroud)

相当于

foo(sub { ... }, ...);
Run Code Online (Sandbox Code Playgroud)