另一个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->($_)
,但所有内容都有相同的结果。
我在这里做错了什么?
答案 0 :(得分:6)
perl具有内置map
功能。我们称之为map2
使用&$f
取消引用$f
使用join
正确打印数组
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_map
和mapn
都使用称为原型的Perl子例程的高级功能。这些原型不是参数验证工具(与许多其他语言一样)。相反,它们告诉perl以特殊方式解释对函数的调用(类似于使用其他一些内置函数的方式)。在这种情况下,原型的&
部分告诉perl这些函数的第一个参数可以写成一个裸块,就像普通的map
调用一样。