В чем разница между grep и map в Perl?

В Perl и grep, и map берут выражение и список и оценивают выражение для каждого элемента списка.

Какая разница между двумя?


person Nathan Fellman    schedule 22.02.2009    source источник


Ответы (6)


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

$ perl -le 'print join " ", grep $_ & 1, (1, 2, 3, 4, 5)'
1 3 5
$ perl -le 'print join " ", map $_ & 1, (1, 2, 3, 4, 5)'
1 0 1 0 1

В первом примере печатаются все нечетные элементы списка, а во втором примере печатается 0 или 1 в зависимости от того, является ли соответствующий элемент нечетным или нет.

person Greg Hewgill    schedule 22.02.2009
comment
Кроме того, map позволяет отображать один-ко-многим, поэтому его выражение находится в контексте списка, а не в скалярном контексте выражения grep. - person ysth; 22.02.2009
comment
@ysth: Это правда. Я собирался сказать что-то о карте, всегда возвращающей то же количество элементов, что и ее аргумент списка, но потом вспомнил, что выражение может не возвращать ни одного или более одного элемента в виде списка. - person Greg Hewgill; 22.02.2009

Я считаю, что полезно подумать о grep() и map() в их самой общей форме:

grep {BLOCK} LIST   
map  {BLOCK} LIST

grep() - это фильтр: он возвращает подмножество элементов из LIST, для которых BLOCK возвращает true.

map() - функция сопоставления: отправляет значение из LIST в BLOCK, а BLOCK возвращает список из 0 или более значений; объединенный набор всех этих вызовов BLOCK будет окончательным списком, возвращаемым map().

person FMc    schedule 22.02.2009

map применяет функцию ко всем элементам в списке и возвращает результат.

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

my %fruits = (
  banana => {
    color => 'yellow',
    price => 0.79,
    grams => 200
  },
  cherry => {
    color => 'red',
    price => 0.02,
    grams => 10
  },
  orange => {
    color => 'orange',
    price => 1.00,
    grams => 225
  }
);

my %red_fruits = map { $_ => $fruits{$_} }
                   grep { $fruits{$_}->{color} eq 'red' }
                     keys(%fruits);

my @prices = map { $fruits{$_}->{price} } keys(%fruits);
my @colors = map { $fruits{$_}->{color} } keys(%fruits);
my @grams  = map { $fruits{$_}->{grams} } keys(%fruits);

# Print each fruit's name sorted by price lowest to highest:
foreach( sort { $fruits{$a}->{price} <=> $fruits{$b}->{price}} keys(%fruits) )
{
  print "$_ costs $fruits{$_}->{price} each\n";
}# end foreach()
person JDrago    schedule 26.02.2009

Еще одна вещь о grep: в скалярном контексте он сообщает вам, сколько элементов было найдено. Это может быть полезно, если вам не нужен второй список, но вы хотите знать, сколько в нем элементов определенного типа.

my @numbers = qw/1 2 3 4 5 6/;

my @odd_numbers  = grep { $_ & 1 } @numbers; # grep returns (1, 3, 5)

my $how_many_odd = grep { $_ & 1 } @numbers; # grep returns 3

Изменить: поскольку OP задал вопрос в комментарии, я должен сказать, что вы можете использовать map в скалярном контексте таким же образом. Дело было не в том, что grep - единственный из двух, кто может это сделать, но в том, что иногда это полезно делать с grep.

person Telemachus    schedule 22.02.2009
comment
не делает ли map то же самое в скалярном контексте? - person Nathan Fellman; 22.02.2009
comment
да. Я не имел в виду, что это делает только grep - просто это хорошая функция, о которой люди иногда не знают. (Кроме того, я не могу придумать случай, когда я хотел бы использовать карту в скалярном контексте.) - person Telemachus; 22.02.2009

Думайте о grep как о карте с фильтром. map выполняет итерацию и дает возможность что-то делать с каждым элементом. Например, эти две строки эквивалентны:

my @copy = @original;
my @copy = map {$_} @original;

Точно так же эти два эквивалента:

my @copy = grep {-f $_} @original;

@copy = ();
for (@original)
{
  push @copy, $_ if -f $_;
}

grep предоставляет возможность вставлять условное выражение и, следовательно, становится фильтром.

person Paul Beckingham    schedule 22.02.2009

Ломко: grep дает контекст своего скаляра блока, map дает контекст списка блоков. (И BLOCK foreach LIST дает своему блоку недействительный контекст.)

person ysth    schedule 19.05.2011