如何创建接受代码块的 Perl 子例程 [英] How to create a Perl subroutine that accepts a block of code

查看:26
本文介绍了如何创建接受代码块的 Perl 子例程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组看起来像这样的子程序:

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

}

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

And I have like four or more that look the same, the only thing that changes is the body of the while block. I'd like to abstract this and stop copy-pasting the code around.

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

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

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. Maybe there is a more intelligent thing to do than what I am trying to accomplish.

推荐答案

Perl 提供了一个称为子程序原型的系统,它允许您编写用户子程序,这些子程序以类似于内置函数的方式进行解析.您想要模拟的内置函数是 mapgrepsort,它们都可以将一个块作为它们的第一个参数.

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. The builtins that you want to emulate are map, grep, or sort which can each take a block as their first argument.

要使用原型来做到这一点,您可以使用 sub name (&) {...} 其中 & 告诉 perl 该函数的第一个参数是块(带或不带 sub),或文字子程序 \&mysub.(&) 原型指定了一个且只有一个参数,如果需要在代码块后传递多个参数,可以写成 (&@)意味着,一个代码块后跟一个列表.

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. 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.

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

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

该子例程将对传入列表的每个元素运行传入块.\&{shift @_} 看起来有点神秘,但它所做的是移出列表的第一个元素,这应该是一个代码块.&{...} 将值作为子例程取消引用(调用任何重载),然后 \ 立即引用它.如果该值是一个 CODE 引用,那么它会原封不动地返回.如果它是一个重载的对象,它就会变成代码.如果无法强制转换为 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. 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. If it was an overloaded object, it is turned into code. If it could not be coerced into CODE, an error is thrown.

要调用这个子程序,你应该写:

To call this subroutine, you would write:

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

(&@) 原型让您将参数编写为 map/grep 之类的块仅在使用更高版本时才有效命令函数作为函数.如果将其用作方法,则应省略原型并按以下方式编写:

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

这篇关于如何创建接受代码块的 Perl 子例程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆