如何实现Perl中的map函数?

时间:2011-10-02 13:07:26

标签: perl map

用Perl编写的Perl中的map函数是什么?我只是无法弄清楚如何实现它。这是我的尝试:

use Data::Dumper;

sub Map {
    my ($function, $sequence) = @_;

    my @result;
    foreach my $item (@$sequence) {
        my $_ = $item;
        push @result, $function->($item);
    }
    return @result
}

my @sample = qw(1 2 3 4 5);
print Dumper Map(sub { $_ * $_ }, \@sample);
print Dumper map({ $_ * $_ } @sample);
$_中的{p> $function未定义,但map如何克服此问题?

3 个答案:

答案 0 :(得分:10)

map有一些特殊的语法,所以你不能在pure-perl中完全实现它,但这会非常接近它(只要你使用map的块形式): / p>

sub Map(&@) {
    my ($function, @sequence) = @_;

    my @result;
    foreach my $item (@sequence) {
        local $_ = $item;
        push @result, $function->($item);
    }
    return @result
}

use Data::Dumper;
my @sample = qw(1 2 3 4 5);
print Dumper Map { $_ * $_ } @sample;
print Dumper map { $_ * $_ } @sample;
使用$_代替local $_可以克服

my $_未定义的问题。实际上你几乎不想使用my $_(即使你确实想在几乎所有其他变量上使用它)。

添加(&@)原型允许您不要在块前面指定sub。同样,你几乎never want to use prototypes,但这是对他们的有效使用。

答案 1 :(得分:5)

虽然接受的答案实现了类似map的函数,但它不会像perl那样执行。 forforeachmapgrep的一个重要部分是它们为您提供的$_始终是参数中值的别名名单。这意味着在任何这些构造中调用s/a/b/之类的东西将修改它们被调用的元素。这允许您编写如下内容:

my ($x, $y) = qw(foo bar);

$_ .= '!' for $x, $y;

say "$x $y"; # foo! bar!

map {s/$/!!!/} $x, $y;

say "$x $y"; # foo!!!! bar!!!!

因为在你的问题中,你曾要求Map使用数组引用而不是数组,这里有一个适用于数组引用的版本,它与内置map一样接近你可以得到的纯粹的Perl。

use 5.010;
use warnings;
use strict;

sub Map (&\@) {
    my ($code, $array) = splice @_;
    my @return;
    push @return, &$code for @$array;
    @return
}

my @sample = qw(1 2 3 4 5);
say join ', ' => Map { $_ * $_ } @sample; # 1, 4, 9, 16, 25
say join ', ' => map { $_ * $_ } @sample; # 1, 4, 9, 16, 25

Map中,(&\@)原型告诉perl Map裸字将使用与通常子例程不同的规则进行解析。 &表示第一个参数将是一个裸块Map {...} NEXT,或者它将是一个文字代码引用Map \&somesub, NEXT。请注意后一版本中的参数之间的逗号。 \@原型表明下一个参数将以@开头,并将作为数组引用传入。

最后,splice @_行会清空@_,而不仅仅是复制值。这样,&$code行会看到空@_而不是收到的Map&$code的原因是它是调用子例程的最快方法,并且与map使用的多字符调用样式非常接近,因为您可以在不使用C的情况下使用。这种调用样式非常适合对于这种用法,因为块的参数在$_中,不需要任何堆栈操作。

在上面的代码中,我做了一点作弊,让for完成本地化$_的工作。这对性能有好处,但是为了看它是如何工作的,这里是重写的那一行:

    for my $i (0 .. $#$array) {   # for each index
        local *_ = \$$array[$i];  # install alias into $_
        push @return, &$code;
    }

答案 2 :(得分:0)

我的Object::Iterate模块就是您要做的事情的一个示例。