当前位置: 动力学知识库 > 问答 > 编程问答 >

perl - Sub references as arguments

问题描述:

Another Perl-beginner question, but strangely enough, I found no tutorial to explain me this simple problem.

I wanted, as an exercise, to write a function map that takes a function and an array, returning an array. In functional languages, this is used quite often and I heard about the sub references and how to use them.

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

But, for some reason, I only get the word CODE and a hex-number as an output, five times. I have then changed the call of f in map, to $$f($_) and $f->($_) but all of it had the same result.

What do I do wrong here?

网友答案:
  1. perl have a builin map function. let's call it map2

  2. use &$f to dereference $f

  3. use join to print an array properly


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
网友答案:

As mentioned in comments, Perl has a built in map function that you should use.

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

Rather than passing an argument, Perl's built in map sets $_ to each element, which allows you to write your square function succinctly as either {$_ * $_} or {$_ ** 2}

But Perl also gives you the ability to make custom map-like functions with a similar syntax. For example, say you wanted to write a version of map that maps over pairs of values:

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;

But since Perl has been around a while, most utility functions have probably been written already. A search of CPAN will turn up many map-like functions that do various things.

I found that I frequently need to map over lists with various step sizes, so I wrote the mapn function in List::Gen. This is a fully developed solution, so it includes an optimization when called in void context, and falls back to Perl's own map when n == 1:

sub mapn (&[email protected]) {
    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
}

Both pair_map and mapn utilize an advanced feature of Perl subroutines called prototypes. These prototypes are not argument validation tools (like in many other languages). Rather, they tell perl to interpret calls to the functions in a special way (similar to the way some other builtin functions are used). In this case, the & portion of the prototype tells perl that the first argument to these functions can be written as a bare block, just like a normal map call.

分享给朋友:
您可能感兴趣的文章:
随机阅读: