Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

# 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 более удобочитаемым.

Ответ 2

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

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);

(Edit: исправлено отсутствие "ключей% params" в этой последней строке)

Ответ 3

Функция 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 {'"' . $_ . '"' } @_ }

Ответ 5

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

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

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

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

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

Ответ 6

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

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

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

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

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

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

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

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

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

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

@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('*');

Ответ 7

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

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

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

@names = map (ucfirst, @names);

Ответ 8

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

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

Обратите внимание, что map совсем не специфично для Perl. Это стандартный метод, используемый функциональными языками. Его можно даже реализовать в C с помощью указателей функций или на С++ с помощью "объектов функций".

Ответ 9

"Просто сахар" суровый. Помните, что цикл - это просто сахар - если и goto может делать все конструкции цикла, и многое другое.

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

Ответ 10

Перефразируя "Эффективное программирование на Perl" Холла и Шварца, карту можно злоупотреблять, но я думаю, что она лучше всего использовалась для создания нового списка из существующего списка.

Создайте список квадратов из 3,2 и 1:

@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;

Ответ 11

Сгенерировать пароль:

$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G

Ответ 12

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

my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";

Ответ 13

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

Например, вы можете сопоставить функцию синтаксического анализа в списке строк, чтобы преобразовать их в целые числа.

Ответ 14

Он позволяет вам преобразовать список как выражение, а не в инструкции. Представьте себе хеш солдат, определенных так:

{ name          => 'John Smith'
, rank          => 'Lieutenant'
, serial_number => '382-293937-20'
};

то вы можете работать в списке имен отдельно.

Например,

map { $_->{name} } values %soldiers

- выражение. Он может идти в любом месте, где разрешено выражение, за исключением того, что вы не можете его назначить.

${[ sort map { $_->{name} } values %soldiers ]}[-1]

индексирует массив, принимая max.

my %soldiers_by_sn = map { $->{serial_number} => $_ } values %soldiers;

Я считаю, что одним из преимуществ операционных выражений является то, что он сокращает количество ошибок, возникающих из временных переменных.

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

my %soldiers_by_sn 
    = map  { $->{serial_number}, $_ } 
      grep { $_->{name} !~ m/Hatfield$/ } 
      values %soldiers
      ;

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

Ответ 15

Как говорили другие, карта создает списки из списков. Подумайте о "отображении" содержимого одного списка в другой. Здесь некоторый код из программы CGI, чтобы взять список номеров патентов и распечатать гиперссылки к патентным заявкам:

my @patents = ('7,120,721', '6,809,505', '7,194,673');
print join(", ", map { "<a href=\"http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL&p=1&u=/netahtml/srchnum.htm&r=0&f=S&l=50&TERM1=$_\">$_</a>" } @patents);

Ответ 16

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

Одно отличие состоит в том, что для неэффективного выражения выражение, которое изменяет список, переименовывается. Один из них завершается, а другой - нет:

perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'

Еще одно небольшое отличие состоит в том, что контекст внутри блока карты представляет собой контекст списка, но цикл for передает контекст void.