В Perl и grep
, и map
берут выражение и список и оценивают выражение для каждого элемента списка.
Какая разница между двумя?
В Perl и grep
, и map
берут выражение и список и оценивают выражение для каждого элемента списка.
Какая разница между двумя?
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 в зависимости от того, является ли соответствующий элемент нечетным или нет.
Я считаю, что полезно подумать о grep()
и map()
в их самой общей форме:
grep {BLOCK} LIST
map {BLOCK} LIST
grep()
- это фильтр: он возвращает подмножество элементов из LIST, для которых BLOCK возвращает true.
map()
- функция сопоставления: отправляет значение из LIST в BLOCK, а BLOCK возвращает список из 0 или более значений; объединенный набор всех этих вызовов BLOCK будет окончательным списком, возвращаемым map()
.
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()
Еще одна вещь о 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
.
Думайте о grep как о карте с фильтром. map выполняет итерацию и дает возможность что-то делать с каждым элементом. Например, эти две строки эквивалентны:
my @copy = @original;
my @copy = map {$_} @original;
Точно так же эти два эквивалента:
my @copy = grep {-f $_} @original;
@copy = ();
for (@original)
{
push @copy, $_ if -f $_;
}
grep предоставляет возможность вставлять условное выражение и, следовательно, становится фильтром.
Ломко: grep дает контекст своего скаляра блока, map дает контекст списка блоков. (И BLOCK foreach LIST дает своему блоку недействительный контекст.)