Какой смысл в карте Perl? - PullRequest
       29

Какой смысл в карте Perl?

30 голосов
/ 26 сентября 2008

Не совсем понял смысл функции карты. Кто-нибудь может объяснить примерами его использования?

Есть ли какие-либо преимущества в производительности при использовании этого вместо цикла или это просто сахар?

Ответы [ 16 ]

44 голосов
/ 26 сентября 2008

Каждый раз, когда вы хотите создать список на основе другого списка:

# Double all elements of a list
my @double = map { $_ * 2 } (1,2,3,4,5);
# @double = (2,4,6,8,10);

Поскольку списки легко конвертируются попарно в хэши, если вам нужна хеш-таблица для объектов на основе определенного атрибута:

# @user_objects is a list of objects having a unique_id() method
my %users = map { $_->unique_id() => $_ } @user_objects;
# %users = ( $id => $obj, $id => $obj, ...);

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

Некоторые могут предпочесть подробный цикличный код для удобства чтения, но лично я считаю map более читабельным.

28 голосов
/ 26 сентября 2008

Прежде всего, это простой способ преобразования массива: вместо того, чтобы говорить, например,

my @raw_values = (...);
my @derived_values;
for my $value (@raw_values) {
    push (@derived_values, _derived_value($value));
}

Вы можете сказать

my @raw_values = (...);
my @derived_values = map { _derived_value($_) } @raw_values;

Это также полезно для создания таблицы быстрого поиска: вместо, например,

my $sentence = "...";
my @stopwords = (...);
my @foundstopwords;
for my $word (split(/\s+/, $sentence)) {
    for my $stopword (@stopwords) {
       if ($word eq $stopword) {
           push (@foundstopwords, $word);
       }
    }
}

Вы могли бы сказать

my $sentence = "...";
my @stopwords = (...);
my %is_stopword = map { $_ => 1 } @stopwords;
my @foundstopwords = grep { $is_stopword{$_} } split(/\s+/, $sentence);

Это также полезно, если вы хотите получить один список из другого, но вам не нужно, чтобы временная переменная загромождала место, например а не

my %params = ( username => '...', password => '...', action => $action );
my @parampairs;
for my $param (keys %params) {
    push (@parampairs, $param . '=' . CGI::escape($params{$param}));
}
my $url = $ENV{SCRIPT_NAME} . '?' . join('&', @parampairs);

Вы говорите, намного проще

my %params = ( username => '...', password => '...', action => $action );
my $url = $ENV{SCRIPT_NAME} . '?'
    . join('&', map { $_ . '=' . CGI::escape($params{$_}) } keys %params);

(Правка: исправлены пропущенные «ключи% params» в последней строке)

21 голосов
/ 26 сентября 2008

Функция map используется для преобразования списков. Это в основном синтаксический сахар для замены определенных типов циклов for[each]. Как только вы обернетесь вокруг него, вы увидите повсюду его использование:

my @uppercase = map { uc } @lowercase;
my @hex       = map { sprintf "0x%x", $_ } @decimal;
my %hash      = map { $_ => 1 } @array;
sub join_csv { join(',', map {'"' . $_ . '"' } @_ }
18 голосов
/ 26 сентября 2008

См. Также преобразование Шварца для расширенного использования карты.

12 голосов
/ 26 сентября 2008

Это также удобно для создания поисковых хешей:

my %is_boolean = map { $_ => 1 } qw(true false);

эквивалентно

my %is_boolean = ( true => 1, false => 1 );

Там не так много сбережений, но предположим, что вы хотели определить %is_US_state?

11 голосов
/ 26 сентября 2008

map используется для создания списка путем преобразования элементов другого списка.

grep используется для создания списка путем фильтрации элементов другого списка.

sort используется для создания списка путем сортировки элементов другого списка.

Каждый из этих операторов получает блок кода (или выражение), который используется для преобразования, фильтрации или сравнения элементов списка.

Для map результатом блока становится один (или более) элемент (ов) в новом списке. Текущий элемент имеет псевдоним $ _.

Для grep логический результат блока решает, будет ли элемент исходного списка скопирован в новый список. Текущий элемент имеет псевдоним $ _.

Для sort блок получает два элемента (с псевдонимами $ a и $ b) и, как ожидается, вернет один из -1, 0 или 1, указывая, больше ли $ a, равно или меньше чем $ b.

Преобразование Шварца использует эти операторы для эффективного кэширования значений (свойств), которые будут использоваться при сортировке списка, особенно когда вычисление этих свойств имеет нетривиальную стоимость.

Он работает путем создания промежуточного массива, который имеет в качестве элементов ссылки на массив с исходным элементом и вычисленным значением, по которому мы хотим отсортировать. Этот массив передается для сортировки, которая сравнивает уже вычисленные значения, создавая другой промежуточный массив (этот отсортирован), который, в свою очередь, передается на другую карту, которая выбрасывает кэшированные значения, тем самым восстанавливая массив до его начальных элементов списка (но в нужном порядке).

Пример (создает список файлов в текущем каталоге, отсортированный по времени их последнего изменения):

@file_list = glob('*');
@file_modify_times = map { [ $_, (stat($_))[8] ] } @file_list;
@files_sorted_by_mtime = sort { $a->[1] <=> $b->[1] } @file_modify_times;
@sorted_files = map { $_->[0] } @files_sorted_by_mtime;

Объединяя операторы вместе, для промежуточных массивов объявление переменных не требуется;

@sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, (stat($_))[8] ] } glob('*');

Вы также можете отфильтровать список перед сортировкой, вставив grep (если вы хотите фильтровать по тому же кешированному значению):

Пример (список файлов, измененных за последние 24 часа, отсортированный по времени последнего изменения):

    @sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } grep { $_->[1] > (time - 24 * 3600 } map { [ $_, (stat($_))[8] ] } glob('*');
5 голосов
/ 16 января 2009

«Просто сахар» суров. Помните, что цикл - это просто сахар - если и goto может делать все, что делают конструкции цикла и даже больше.

Карта - это функция достаточно высокого уровня, которая помогает вам выполнять намного более сложные операции в своей голове, чтобы вы могли кодировать и устранять более серьезные проблемы.

5 голосов
/ 26 сентября 2008

Функция карты является идеей из парадигмы функционального программирования. В функциональном программировании функции являются первоклассными объектами, что означает, что они могут быть переданы в качестве аргументов другим функциям. Карта - простой, но очень полезный пример этого. В качестве аргументов он принимает функцию (назовем ее f) и список l. f должна быть функцией, принимающей один аргумент, и map просто применяет f к каждому элементу списка l. f может делать все, что вам нужно, с каждым элементом: добавлять по одному для каждого элемента, помещать в квадрат каждый элемент, записывать каждый элемент в базу данных или открывать окно веб-браузера для каждого элемента, который является допустимым URL-адресом.

Преимущество использования map состоит в том, что он красиво инкапсулирует итерации по элементам списка. Все, что вам нужно сделать, это сказать «сделать f каждому элементу», и map может решить, как лучше всего это сделать. Например, можно реализовать map, чтобы разделить его работу между несколькими потоками, и это было бы абсолютно прозрачно для звонящего.

Обратите внимание, что map совсем не специфичен для Perl. Это стандартная техника, используемая функциональными языками. Это даже может быть реализовано в C с использованием указателей на функции или в C ++ с использованием «функциональных объектов».

5 голосов
/ 26 сентября 2008

Функция map запускает выражение для каждого элемента списка и возвращает результаты списка. Допустим, у меня был следующий список

@names = ("andrew", "bob", "carol" );

и я хотел использовать заглавные буквы в каждом из этих имен. Я мог бы просмотреть их и вызвать ucfirst каждого элемента, или я мог бы просто сделать следующее

@names = map (ucfirst, @names);
4 голосов
/ 26 сентября 2008

Вы используете карту для преобразования списка и назначения результатов другому списку, grep для фильтрации списка и назначения результатов другому списку. «Другой» список может быть той же переменной, что и список, который вы трансформируете / фильтруете.

my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...