Как реализована функция карты в Perl? - PullRequest
3 голосов
/ 02 октября 2011

Является ли map функция на Perl написана на 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);

$_ в $function не определено, как должно быть, но как map преодолевает это?

Ответы [ 3 ]

10 голосов
/ 02 октября 2011

map имеет некоторый специальный синтаксис, поэтому вы не можете полностью реализовать его в чистом perl, но это очень близко к нему (если вы используете блочную форму карты):

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 перед блоком. Опять же, вы почти никогда не захотите использовать прототипы , но это допустимое использование.

5 голосов
/ 03 октября 2011

Хотя принятый ответ реализует 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!!!!

Поскольку в вашем вопросе вы попросили 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 заключается в том, что это самый быстрый способ вызова подпрограммы, и он настолько близок к стилю вызова multicall, который использует map, насколько вы можете получить без использования C. Этот стиль вызова идеально подходит для этого использования,поскольку аргумент блока находится в $_, что не требует каких-либо манипуляций со стеком.

В приведенном выше коде я немного изменяю и позволяю for выполнить работу по локализации $_.Это хорошо для производительности, но чтобы увидеть, как это работает, вот эта строка переписана:

    for my $i (0 .. $#$array) {   # for each index
        local *_ = \$$array[$i];  # install alias into $_
        push @return, &$code;
    }
0 голосов
/ 02 октября 2011

Мой Object :: Iterate модуль является примером того, что вы пытаетесь сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...