В чем смысл карты Perl?

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

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


person Brian G    schedule 25.09.2008    source источник


Ответы (15)


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

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

person Adam Bellaire    schedule 25.09.2008
comment
Я считаю карты более читаемыми, чем петли. Многословие не обязательно улучшает читаемость. - person Svante; 17.11.2008
comment
Я согласен с вами, я хотел оставить это решение на усмотрение читателя. - person Adam Bellaire; 17.11.2008
comment
При тестировании функция карты в скриптах, кажется, выполняется намного быстрее, чем цикл. - person DevilWAH; 09.08.2012
comment
Карта - это тоже чистое решение проблемы. For - это утверждение, а map - выражение. Определение действий в виде выражений, а не операторов, уменьшает побочные эффекты и позволяет гораздо проще и с меньшими проблемами создавать код, который может быть разделен на потоки или разветвлен. - person Horus; 08.12.2014

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

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" в последней строке)

person Sam Kington    schedule 25.09.2008
comment
Я думаю, что вы оставили ключи% params из своего последнего примера. Кроме того, ваш пример стоп-слов, вероятно, лучше было бы сделать с регулярным выражением, а не с split / grep. - person cjm; 26.09.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 {'"' . $_ . '"' } @_ }
person Michael Carman    schedule 25.09.2008

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

person toolkit    schedule 25.09.2008

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

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

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

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

Здесь не так много экономии, но предположим, вы хотите определить %is_US_state?

person cjm    schedule 25.09.2008
comment
Однако Set :: Object быстрее и имеет лучший API. - person jrockway; 18.10.2009

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

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

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

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

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

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

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

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

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

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

@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('*');
person kixx    schedule 26.09.2008

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

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

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

@names = map (ucfirst, @names);
person Craig H    schedule 25.09.2008

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

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

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

person Dima    schedule 26.09.2008

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

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

person Darron    schedule 16.01.2009

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

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

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

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

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

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

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

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

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

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

Например,

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

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

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

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

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

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

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

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

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

person Community    schedule 25.09.2008

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

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

person Community    schedule 25.09.2008

Как говорили другие, карта создает списки из списков. Подумайте о «отображении» содержимого одного списка в другой. Вот код из программы 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);
person jimtut    schedule 26.09.2008