前段时间我被问到“奇怪”的问题,我如何用map
实施grep
。
今天我试着去做,这就是出来的。我是否从Perl中挤出了所有东西,还是有其他更聪明的黑客?
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
sub my_map(&@) {
grep { $_= $_[0]->($_) } @_[1..$#_];
}
my @arr = (1,2,3,4);
#list context
say (my_map sub {$_+1}, @arr);
#scalar context
say "".my_map {$_+1} @arr;
say "the array from outside: @arr";
say "builtin map:", (map {$_+1} @arr);
答案 0 :(得分:10)
您确定他们没有询问如何使用grep
实施map
吗?这有时候非常有用。
grep { STMTs; EXPR } LIST
可以写成
map { STMTs; EXPR ? $_ : () } LIST
(但有一点不同:grep
会返回左值,而map
则不会。)
知道这一点,可以压缩
map { $_ => 1 } grep { defined } @list
到
map { defined ? $_ => 1 : () } @list
(我更喜欢“未压缩”版本,但“压缩”版本可能会快一点。)
至于使用map
实现grep
,您可以利用grep
的循环和别名属性。
map { STMTs; EXPR } LIST
可以写成
my @rv;
grep { STMTs; push @rv, EXPR } LIST;
@rv
答案 1 :(得分:3)
我在这个毫无意义的学术活动中的尝试是。
sub my_map (&@) {
my $code = shift;
my @return_list;
grep {
push @return_list, $code->($_);
} @_;
return @return_list;
}
使用grep
这有点浪费,因为地图的返回列表与输入列表可能不是1:1,如my %hash = map { $_ => 1 } @array;
,你需要更通用的使用grep的返回列表。结果是允许修改原始列表的任何查找方法都可以。
sub my_map (&@) {
my $code = shift;
my @return_list;
push @return_list, $code->($_) for @_;
return @return_list;
}
答案 2 :(得分:1)
我不完全确定你的意思(map
的哪个方面应由grep
模仿),但典型的地图方案可能是y = x**3
:
...
my @list1 = (1,2,3,4,5);
my @list2 = map $_**3, @list1;
...
使用grep
,如果必须,您可以几乎使其像map
一样隐藏(但会破坏原始列表):
...
my @list2 = grep { $_**=3; 1 } @list1;
...
只需写入原始列表元素的引用即可。这里的问题是对原始列表的不需要的修改;这是你不想对map
。
因此,我们可以在子例程中生成另一个列表,修改此列表并保持原始列表不变。稍微修改一下Sinan Ünür's solution,就会看到:
sub gap(&@) {
my $code = shift;
my @list = @_;
grep $_ = $code->($_), @list;
@list
}
my @arr = (1 .. 5);
# original map
print join ',', map { $_**3 } @arr;
# 1,8,27,64,125
# grep map
print join ',', gap { $_**3 } @arr;
# 1,8,27,64,125
# test original array
print join ',', @arr;
# 1,2,3,4,5 => untouched
此致
RBO