如何实现Perl中的map函数?

ale*_*kuk 3 perl map

mapPerl中的函数是用Perl编写的吗?我只是无法弄清楚如何实现它.这是我的尝试:

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);
Run Code Online (Sandbox Code Playgroud)

$_in $function是未定义的,但是如何map克服这个?

Leo*_*ans 10

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

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;
Run Code Online (Sandbox Code Playgroud)

$_通过使用local $_而不是来克服未定义my $_.实际上你几乎从不想使用my $_(即使你确实想在几乎所有其他变量上使用它).

添加(&@)原型允许您不要sub在块前面指定.同样,你几乎从不想使用原型,但这是对它们的有效使用.


Eri*_*rom 5

虽然接受的答案实现了类似map函数,但它不会像perl那样执行.的一个重要组成部分for,foreach,map,和grep是,$_他们提供给你的是永远的别名在参数列表中的值.这意味着调用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!!!!
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

在原型中Map,(&\@)原型告诉perl Map将使用与通常的子例程不同的规则来解析裸字.的&指示的第一个参数要么是裸块Map {...} NEXT或这将是一个文字代码引用Map \&somesub, NEXT.请注意后一版本中的参数之间的逗号.所述\@原型表示下一个参数将开始与@和作为数组引用将被传递英寸

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

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

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