子引用作为参数

时间:2011-01-18 04:31:09

标签: perl

另一个Perl-beginner问题,但奇怪的是,我找不到教程来解释这个简单的问题。

作为练习,我希望编写一个函数map,它接受​​一个函数和一个数组,返回一个数组。在函数式语言中,经常使用它,我听说了子引用以及如何使用它们。

sub map {
  my $f = shift;
  my @r = ();

  foreach (@_) {
    push(@r, &f($_));
  }
  return @r;
}

sub square {
  my $r = shift;
  return $r*$r;
}

print map(\&shift, 1, 2, 3, 4, 5);

但是,出于某种原因,我只得到单词CODE和十六进制数作为输出,五次。然后,我将f中的map调用更改为$$f($_)$f->($_),但所有内容都有相同的结果。

我在这里做错了什么?

2 个答案:

答案 0 :(得分:6)

  1. perl具有内置map功能。我们称之为map2

  2. 使用&$f取消引用$f

  3. 使用join正确打印数组


  4. sub map2 {
      my $f = shift;
      my @r = ();
    
      foreach (@_) {
        push(@r, &$f($_));
      }
      return @r;
    }
    
    sub square {
      my $r = shift;
      return $r*$r;
    }
    
    print join ",", map2(\&square, 1, 2, 3, 4, 5);
    

    $ perl 1.pl
    1,4,9,16,25
    

答案 1 :(得分:1)

正如评论中所提到的,Perl有一个你应该使用的内置map函数。

my @squares = map {$_ ** 2} 1 .. 5;

Perl内置map而不是传递参数,而是将$_设置为square每个元素,这使您可以简洁地将{$_ * $_}函数编写为{$_ ** 2}或{{ 1}}

但是Perl还使您能够使用类似的语法制作自定义类似地图的函数。例如,假设您想要编写映射成对值的map版本:

sub pair_map (&@) {     # the (&@) prototype here tells perl that the sub
    my $code = shift;   # takes a code block, and then a list, just like `map`
    my @ret;        
    while (@_) {
        push @ret, $code->(splice @_, 0, 2);
    }
    @ret
}

my @pairs = pair_map {\@_} 1 .. 10;

pair_map {print "$_[0]: $_[1]\n"} %hash;

但是由于Perl已经存在了一段时间,大多数实用功能可能已经写好了。搜索CPAN将会发现许多map - 类似于执行各种操作的函数。

我发现我经常需要映射具有不同步长的列表,因此我在List::Gen中编写了mapn函数。这是一个完全开发的解决方案,因此它在void上下文中调用时包含优化,并在map时回退到Perl自己的n == 1

sub mapn (&$@) {
    my ($sub, $n, @ret) = splice @_, 0, 2;
    croak '$_[1] must be >= 1' unless $n >= 1;

    return map $sub->($_) => @_ if $n == 1;

    my $want = defined wantarray;
    while (@_) {
        local *_ = \$_[0];
        if ($want) {push @ret =>
              $sub->(splice @_, 0, $n)}
        else {$sub->(splice @_, 0, $n)}
    }
    @ret
}

pair_mapmapn都使用称为原型的Perl子例程的高级功能。这些原型不是参数验证工具(与许多其他语言一样)。相反,它们告诉perl以特殊方式解释对函数的调用(类似于使用其他一些内置函数的方式)。在这种情况下,原型的&部分告诉perl这些函数的第一个参数可以写成一个裸块,就像普通的map调用一样。