简体   繁体   English

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

[英]How to create a Perl subroutine that accepts a block of code

I have a set of subroutines that look like this:我有一组如下所示的子例程:

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();

}

And I have like four or more that look the same, the only thing that changes is the body of the while block.我有四个或更多看起来相同的,唯一改变的是 while 块的主体。 I'd like to abstract this and stop copy-pasting the code around.我想对此进行抽象并停止复制粘贴代码。

  • Is there a way to code a subroutine that accepts a block of code and executes it?有没有办法编写一个接受代码块并执行它的子程序?

To give more context, the different foo subroutines are a different Finite State Machine (FSM) that read the contents of different files and feed the data to a hash reference.为了提供更多上下文,不同的 foo 子例程是不同的有限 State 机器 (FSM),它读取不同文件的内容并将数据提供给 hash 参考。 Maybe there is a more intelligent thing to do than what I am trying to accomplish.也许有比我想要完成的更聪明的事情去做。

Perl offers a system called subroutine prototypes that allow you to write user subs that get parsed in a way similar to the builtin functions. Perl 提供了一个称为子程序原型的系统,它允许您编写用户子程序,并以类似于内置函数的方式进行解析。 The builtins that you want to emulate are map , grep , or sort which can each take a block as their first argument.您要模拟的内置函数是mapgrepsort ,它们每个都可以将块作为第一个参数。

To do that with prototypes, you use sub name (&) {...} where the & tells perl that the first argument to the function is either a block (with or without sub ), or a literal subroutine \&mysub .要使用原型做到这一点,您可以使用sub name (&) {...}其中&告诉 perl function 的第一个参数是一个块(有或没有sub ),或者是一个文字子例程\&mysub The (&) prototype specifies one and only one argument, if you need to pass multiple arguments after the code block, you could write it as (&@) which means, a code block followed by a list. (&)原型指定了一个且只有一个参数,如果您需要在代码块之后传递多个 arguments,您可以将其写为(&@) ,即代码块后跟列表。

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

    for (@_) {
        $code->($_);
    }
}

That subroutine will run the passed in block on every element of the passed in list.该子例程将在传入列表的每个元素上运行传入块。 The \&{shift @_} looks a bit cryptic, but what it does is shifts off the first element of the list, which should be a code block. \&{shift @_}看起来有点神秘,但它所做的是将列表的第一个元素移出,这应该是一个代码块。 The &{...} dereferences the value as a subroutine (invoking any overloading), and then the \ immediately takes the reference to it. &{...}将该值作为子例程取消引用(调用任何重载),然后\立即获取对它的引用。 If the value was a CODE ref, then it is returned unchanged.如果该值是一个 CODE ref,那么它会原封不动地返回。 If it was an overloaded object, it is turned into code.如果它是一个重载的 object,它就会变成代码。 If it could not be coerced into CODE, an error is thrown.如果无法强制转换为 CODE,则会引发错误。

To call this subroutine, you would write:要调用此子例程,您将编写:

higher_order_fn {$_ * 2} 1, 2, 3;
# or
higher_order_fn(sub {$_ * 2}, 1, 2, 3);

The (&@) prototype that lets you write the argument as a map / grep like block only works when using the higher order function as a function. The (&@) prototype that lets you write the argument as a map / grep like block only works when using the higher order function as a function. If you are using it as a method, you should omit the prototype and write it this way:如果您将其用作方法,则应省略原型并这样编写:

sub higher_order_method {
    my $self = shift;
    my $code = \&{shift @_};
    ...
    $code->() for @_;
}
...
$obj->higher_order_method(sub {...}, 'some', 'more', 'args', 'here');
sub bar {
   my ($coderef) = @_;
   ⁝
   $coderef->($f, @arguments);
   ⁝
}

bar(sub { my ($f) = @_; while … }, @other_arguments);

or perhaps a bit less tangled with a named coderef:或者可能与命名的 coderef 纠缠不清:

my $while_sub = sub {
    my ($f) = @_;
    while …
    ⁝
};
bar($while_sub, @other_arguments);

Edit : The Higher-Order Perl book is full of this sort of programming.编辑高阶 Perl书中充满了这种编程。

You want the & prototype.你想要&原型。

sub foo(&@) {
    my ($callback) = shift;
    ...
    $callback->(...);
    ...
}

makes使

foo { ... } ...;

equivalent to相当于

foo(sub { ... }, ...);

Although others already answered the question I was still missing the reference to the official Perl documentation.尽管其他人已经回答了这个问题,但我仍然缺少对官方 Perl 文档的参考。

http://perldoc.perl.org/perlsub.html#Prototypes http://perldoc.perl.org/perlsub.html#Prototypes

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM