在Perl中,如何使用grep实现map?

时间:2012-06-25 13:52:43

标签: perl map grep

前段时间我被问到“奇怪”的问题,我如何用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);

3 个答案:

答案 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