我真的很喜欢 List::MoreUtils 并且经常使用它。但是,我从不喜欢natatime 函数。它不会产生可用于 for 循环或 map 或 grep 的输出。
我喜欢在我的代码中链接 map/grep/apply 操作。一旦您了解了这些功能的工作原理,它们就会非常富有表现力并且非常强大。
但很容易让函数像 natatime 一样返回数组引用列表。
sub group_by ($@) {
my $n = shift;
my @array = @_;
croak "group_by count argument must be a non-zero positive integer"
unless $n > 0 and int($n) == $n;
my @groups;
push @groups, [ splice @array, 0, $n ] while @array;
return @groups;
}
现在你可以这样做了:
my @grouped = map [ reverse @$_ ],
group_by 3, @array;
** 更新 Chris Lutz 的建议 **
克里斯,我可以看到您建议在界面中添加代码参考的优点。这样就内置了类似地图的行为。
# equivalent to my map/group_by above
group_by { [ reverse @_ ] } 3, @array;
这很好,简洁。但是为了保持良好的 {} 代码引用语义,我们将 count 参数 3 放在一个难以看到的位置。
我认为我更喜欢我最初写的东西。
链式地图并不比我们使用扩展 API 获得的详细得多。
使用原始方法,无需重新实现即可使用 grep 或其他类似函数。
例如,如果将代码引用添加到 API,那么您必须这样做:
my @result = group_by { $_[0] =~ /foo/ ? [@_] : () } 3, @array;
得到等价于:
my @result = grep $_->[0] =~ /foo/,
group_by 3, @array;
因为我建议这个是为了方便链接,所以我更喜欢原来的。
当然,允许任何一种形式都很容易:
sub _copy_to_ref { [ @_ ] }
sub group_by ($@) {
my $code = \&_copy_to_ref;
my $n = shift;
if( reftype $n eq 'CODE' ) {
$code = $n;
$n = shift;
}
my @array = @_;
croak "group_by count argument must be a non-zero positive integer"
unless $n > 0 and int($n) == $n;
my @groups;
push @groups, $code->(splice @array, 0, $n) while @array;
return @groups;
}
现在任何一种形式都应该可以工作(未经测试)。我不确定我更喜欢原始 API,还是更喜欢具有内置地图功能的 API。
有人想吗?
** 再次更新**
Chris 指出可选的代码参考版本会迫使用户这样做是正确的:
group_by sub { foo }, 3, @array;
这不太好,违反了预期。由于没有办法拥有灵活的原型(据我所知),因此将 kibosh 放在扩展 API 上,我会坚持使用原始原型。
顺便说一句,我从备用 API 中的匿名 sub 开始,但我将其更改为命名 sub,因为我对代码的外观感到微妙的困扰。没有真正的充分理由,只是直觉反应。我不知道这是否重要。