Perl-向Sub发送块时的最佳实践

Perl-向Sub发送块时的最佳实践,perl,Perl,我非常喜欢函数式编程,所以当我在Perl中发现块引用时,我开始大量使用它们 但是,我编写的以块为参数的函数是这样编写的: sub mygrep (&@) { my $code = shift; my @result; foreach $_ (@_) { push(@result, $_) if &$code; } @result; } (来自) 本质上,我的大多数函数都设置了$\uuu,以便代码块能够访问我的sub中的数据

我非常喜欢函数式编程,所以当我在Perl中发现块引用时,我开始大量使用它们

但是,我编写的以块为参数的函数是这样编写的:

sub mygrep (&@) {
    my $code = shift;
    my @result;
    foreach $_ (@_) {
        push(@result, $_) if &$code;
    }
    @result;
}
(来自)

本质上,我的大多数函数都设置了
$\uuu
,以便代码块能够访问我的sub中的数据。我想我的问题可以分为三个子问题:

  • 这种方法是否存在一些重大缺陷
  • 在设置之前将
    local
    ize$\uu化是一个更好的主意吗
  • 我应该使用部分应用的函数吗

  • 我还是一个Perl新手,所以任何答案和建议都非常感谢-提前感谢!)

    $本地化绝对更好。subref可以修改
    $的值,这些更改将传播到调用函数中。这在
    mygrep()
    案例中不是问题,但在其他案例中可能存在问题。

    使用
    $code->($arg)
    怎么样

    我还没有对它进行测试,但我认为这是可行的,它可以让您向
    $code
    传递额外的参数

    更新:这看起来很有趣,所以我继续测试它。它工作得很好,见下文(我非常不喜欢原型,所以我删除了它,特别是当它不断抱怨@a不是数组引用时--(

    当然,这种思路的主要乐趣也是生成代码

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    sub mygrep {
        my $code = shift;
        my $filter= shift;
        my @result;
        foreach my $arg (@_) {
            push(@result, $arg) if $code->( $arg);
        }
        @result;
    }
    
    my @a= ( 1, 2, 3, 4, 5, 6, 7, 8, 9);
    print mygrep( mod_filter( 3), @a), "\n"; 
    print mygrep( mod_filter( 4), @a), "\n"; 
    
    sub mod_filter
      { my( $filter)= @_;
        return sub { ! (shift() % $filter) };
      }
    

    在您编写的代码中:

    sub mygrep (&@) {
        my $code = shift;
        my @result;
        foreach $_ (@_) {
            push(@result, $_) if &$code;
        }
        @result;
    }
    
    foreach
    循环在每次循环迭代中都隐式地本地化了
    $\ucode>变量。这是非常安全的(也是正确地将值输入
    $\ucode>的最快方法)

    我对上面代码的唯一缺点是每次执行
    &$code
    时,它都可以访问源参数列表,这可能会导致错误。您可以按如下方式重写代码:

    sub mygrep (&@) {
        my $code = shift;
        my @result;
        foreach $_ (splice @_) {
            push(@result, $_) if &$code;  # @_ is empty here
        }
        @result;
    }
    
    以下是编写该函数的其他几种方法:

    sub mygrep (&@) {
        my ($code, @result) = shift;
        &$code and push @result, $_ for splice @_;
        @result
    }
    
    sub mygrep (&@) {
        my $code = shift;
        # or using grep in our new grep:
        grep &$code, splice @_
    }
    
    这些示例中的每一个都为其子例程提供了一个别名
    $\ucode>,并进行了适当的本地化

    如果您对高阶函数感兴趣,我建议您看看我在CPAN上的模块,它提供了几十个高阶函数来处理实列表和惰性列表

    use List::Gen;
    
    my $list = filter {$_ % 2} <1..>;
    
    # as a lazy array:
    say "@$list[0 .. 5]"; # 1 3 5 7 9 11
    
    # as an object:
    $list->map('**2')->drop(100)->say(5); # 40401 41209 42025 42849 43681
    
    zip('.' => <a..>, <1..>)->say(5);  # a1 b2 c3 d4 e5
    
    使用列表::Gen;
    my$list=过滤器{$\u%2};
    #作为惰性数组:
    说“@$list[0..5]”#1 3 5 7 9 11
    #作为一个对象:
    $list->map('**2')->drop(100)->say(5);4040141209420254284943681
    zip('.=>,)->说(5);#a1 b2 c3 d4 e5
    
    1.这种方法是否存在一些主要缺陷?
  • 查看该块时,
    my$\u;
    将隐藏您对包变量
    $\ucode>的更改。从
    mygrep
    内部无法执行此操作

  • map { $_ % 2 ? $_+1 : () } LIST
    
  • &$code
    非常特殊。您需要
    &$code()
    $code->()

  • 更改
    $\uu
    将更改传递给
    mygrep
    的参数。这在这里是不可取的

  • 2.在设置$之前将其本地化是否更好?
    for
    提供了比
    local
    更好的本地化,但它也提供了这里不需要的别名

    3.我应该使用部分应用的函数吗? 我不知道那是什么意思


    固定的:

    sub mygrep (&@) {
        my $code = shift;
        my @result;
        for (@_) {
           # Create copy so $_ can be modified safely.
           for (my $s = $_) {
              push @result, $_ if $code->();
           }
        }
    
        return @result;
    }
    

    也就是说,我认为
    mygrep
    是毫无意义的,因为
    map
    +
    grep
    已经可以更轻松地完成您想要的任务。比较

    mygrep { if ($_ % 2) { ++$_; 1 } else { 0 } } LIST
    

    您甚至可以合并
    map
    grep

    map { $_ % 2 ? $_+1 : () } LIST
    

    是的,这就是我想做的;我已经编写了一些函数,它们看起来像是在List::MoreUtils中找到的,在这里,模仿内置的语法(如grep和map)是很自然的。对于这些函数,我想我会继续使用$\ux,但对于我的其他函数,我认为$code->($arg)是最好的选择:)从可读性的角度来看,我不喜欢
    &$code
    ,它没有说“函数调用”。所以我会写
    $code->()
    ,但这可能是我个人的偏好。可读性是的,但是当放在循环的中心时,
    &$code
    $code->()
    因为它避免了在每次循环迭代中将
    @
    本地化为空列表。@Eric Strom很公平,你是对的。我仍然不喜欢;--)re“特别是当它不断抱怨@a不是数组引用时”,只有在原型中将
    @
    更改为
    \@
    时才会发生这种情况。感谢您提供有关List::Gen的提示!CPAN是如此巨大,有时很难找到好的部分。。。
    map { $_+1 } grep { $_ % 2 } LIST
    
    map { $_ % 2 ? $_+1 : () } LIST