2

前段时间有人问我一个“奇怪”的问题,我将如何map使用grep. 今天我试着去做,结果就是这样。我是从 Perl 中挤出所有东西,还是有其他更聪明的 hack?

#!/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);
4

3 回答 3

11

您确定他们没有问如何实施grepwithmap吗?这有时真的很有用。

grep { STMTs; EXPR } LIST

可以写成

map { STMTs; EXPR ? $_ : () } LIST

(有一个区别:grep返回左值,而map不是。)

知道了这一点,就可以压缩

map { $_ => 1 } grep { defined } @list

map { defined ? $_ => 1 : () } @list

(我更喜欢“未压缩”版本,但“压缩”版本可能会快一点。)


至于实现mapusing grep,您可以利用grep的循环和别名属性。

map { STMTs; EXPR } LIST

可以写成

my @rv;
grep { STMTs; push @rv, EXPR } LIST;
@rv
于 2012-06-25T15:26:24.753 回答
3

我在这个基本上毫无意义的学术活动上的尝试是。

sub my_map (&@) {
    my $code = shift;
    my @return_list;
    grep {
        push @return_list, $code->($_);
    } @_;
    return @return_list;
}

grep这个有点浪费,因为map的返回列表与输入列表可能不是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;
}
于 2012-06-25T15:13:49.923 回答
1

我不完全确定您的意思(map要模拟 ist 的哪个方面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 的解决方案稍作修改,内容如下:

  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

于 2012-06-25T15:45:37.100 回答