如何使用与 List::Util 中的 sort 或 reduce 相同的签名编写子代码
How to code a sub with the same signature as sort or reduce in List::Util
我希望能够像这样使用一个函数:
my @grouped = split_at {
$a->{time}->strftime("%d-%b-%Y %H") ne
$b->{time}->strftime("%d-%b-%Y %H")
} @files;
其中 split_at
根据函数将数组拆分为数组引用数组,如下所示:
sub split_at(&@) {
# split an array into an arrayrefs based on a function
my $cb = shift;
my @input = @_;
my @retval = ( [ ] );
$i = 0;
while ($i <= $#input) {
push @{$retval[$#retval]}, $input[$i];
$i++;
if (($i < $#input) && $cb->($input[$i-1], $input[$i])) { push @retval, [] }
}
pop @retval unless @{$retval[$#retval]};
return @retval;
}
目前我只能这样称呼它:
my @grouped = split_at {
$_[0]->{time}->strftime("%d-%b-%Y %H") ne
$_[1]->{time}->strftime("%d-%b-%Y %H")
} @files;
这里使用 Time::Piece
.
按 mtime 小时对文件进行批处理
我正在尝试找出能够将其称为(简体)的方法:
my @foo = split_at { $a <=> $b } @foo;
与sort
或List::Util::reduce
类似
我已经检查了 List::Util::PP 中要减少的代码以供参考,但我对它的理解还不足以将其移植到我的案例中。
您需要做的主要事情是将词法的第一个和第二个值作为 $a
和 $b
分配给调用者的命名空间。 reduce
使用一个值执行此操作:
# /- 1
# | /-3
# | | /-4
# 0 | | |
local *{"${pkg}::a"} = $a;
# \-----------/
# 2
让我们快速看一下:
local
覆盖此范围内的全局变量以及临时包含在其中的所有范围。因此,当我们调用回调时,该变量将具有不同的值。请记住 $a
和 $b
是特殊的全局变量。
它使用 glob *
分配到该名称空间中的符号 table,它足够聪明地找到正确的 slot 在符号 table 中。想象一个抽屉柜,其中一个抽屉用于标量,一个用于数组,一个用于散列等等。您可能也见过这种安装 subs 的语法。
{""}
语法允许从多个片段和其他变量中构建一个变量名。我们使用包和变量名 a
来获取,例如 main::a
。这需要关闭 strict 'refs'
,否则 perl 会报错,所以代码中有一个 no strict 'refs'
。 1 中的 *
表示我们将此 var 名称用作 glob 类型。
这与 1 类似,但这次使用标量槽。在这种情况下,我们不必禁用严格引用,因为它只是一个普通字符串,而且 Perl 认为这是安全的。这是告诉解释器变量名结束的语法。比较这两个:
my $foo, $foo_bar;
"$foo_bar_baz"; # variable doesn't exist error
"${foo}_bar_baz"; # prints value of $foo and string _bar_baz
"${foo_bar}_baz"; # prints value of $foo_bar and string _baz
我们需要这个,这样我们就不会在包 pkg
.
中获取 $a
的值
我们将词法 $a
的引用分配给类型 glob 中从 1 开始的那个槽。这将是一个标量引用。本质上,我们的变量 $a
现在有了另一个名称 $pkg::a
。当您说 use Foo 'bar'
.
时,这类似于将 sub 导入您的命名空间
看完之后,我们可以更新您的代码。
sub split_at(&@) {
my $cb = shift;
my @input = @_;
my @retval = [];
my $pkg = caller;
my $max = $#input;
for my $i (0 .. $max) {
push @{$retval[$#retval]}, $input[$i];
no strict 'refs';
local *{"${pkg}::a"} = $input[$i];
local *{"${pkg}::b"} = $input[$i + 1]; # autovivification!
if (($i < $max) && $cb->()) {
push @retval, [];
}
}
pop @retval unless @{$retval[$#retval]};
return @retval;
}
my @files = map { { foo => $_ } } qw/ a a b b c d /;
my @grouped = split_at {
$a->{foo} ne
$b->{foo}
} @files;
我已经为我们的示例简化了数据结构。
对于循环的每次迭代,我们都需要 $a
和 $b
。因为通过类型 glob 将下一个元素分配给我们正在查看的 $b
会导致自动生成,所以我不得不将循环类型更改为计数器并引入一个新变量 $max
。我在构建它时 运行 进入了一个无限循环,因为它一直在 @input
.
的末尾放置 undef
个元素
除此之外,代码几乎相同。我们不再需要向回调传递参数,我们需要 no strict 'refs'
。您通常会为尽可能小的范围关闭 strict
。我们还需要获取 caller
的命名空间,以便我们可以将变量放在那里。
您还需要注意一件事。 List::Util::PP sets up $a
和 $b
在调用者的命名空间中,否则我们可能会导致警告,因此如果您想将此函数放入库中,您应该使用相同的代码。
sub import {
my $pkg = caller;
# (RT88848) Touch the caller's $a and $b, to avoid the warning of
# Name "main::a" used only once: possible typo" warning
no strict 'refs';
${"${pkg}::a"} = ${"${pkg}::a"};
${"${pkg}::b"} = ${"${pkg}::b"};
goto &Exporter::import;
}
我希望能够像这样使用一个函数:
my @grouped = split_at {
$a->{time}->strftime("%d-%b-%Y %H") ne
$b->{time}->strftime("%d-%b-%Y %H")
} @files;
其中 split_at
根据函数将数组拆分为数组引用数组,如下所示:
sub split_at(&@) {
# split an array into an arrayrefs based on a function
my $cb = shift;
my @input = @_;
my @retval = ( [ ] );
$i = 0;
while ($i <= $#input) {
push @{$retval[$#retval]}, $input[$i];
$i++;
if (($i < $#input) && $cb->($input[$i-1], $input[$i])) { push @retval, [] }
}
pop @retval unless @{$retval[$#retval]};
return @retval;
}
目前我只能这样称呼它:
my @grouped = split_at {
$_[0]->{time}->strftime("%d-%b-%Y %H") ne
$_[1]->{time}->strftime("%d-%b-%Y %H")
} @files;
这里使用 Time::Piece
.
我正在尝试找出能够将其称为(简体)的方法:
my @foo = split_at { $a <=> $b } @foo;
与sort
或List::Util::reduce
我已经检查了 List::Util::PP 中要减少的代码以供参考,但我对它的理解还不足以将其移植到我的案例中。
您需要做的主要事情是将词法的第一个和第二个值作为 $a
和 $b
分配给调用者的命名空间。 reduce
使用一个值执行此操作:
# /- 1
# | /-3
# | | /-4
# 0 | | |
local *{"${pkg}::a"} = $a;
# \-----------/
# 2
让我们快速看一下:
local
覆盖此范围内的全局变量以及临时包含在其中的所有范围。因此,当我们调用回调时,该变量将具有不同的值。请记住$a
和$b
是特殊的全局变量。它使用 glob
*
分配到该名称空间中的符号 table,它足够聪明地找到正确的 slot 在符号 table 中。想象一个抽屉柜,其中一个抽屉用于标量,一个用于数组,一个用于散列等等。您可能也见过这种安装 subs 的语法。{""}
语法允许从多个片段和其他变量中构建一个变量名。我们使用包和变量名a
来获取,例如main::a
。这需要关闭strict 'refs'
,否则 perl 会报错,所以代码中有一个no strict 'refs'
。 1 中的*
表示我们将此 var 名称用作 glob 类型。这与 1 类似,但这次使用标量槽。在这种情况下,我们不必禁用严格引用,因为它只是一个普通字符串,而且 Perl 认为这是安全的。这是告诉解释器变量名结束的语法。比较这两个:
my $foo, $foo_bar; "$foo_bar_baz"; # variable doesn't exist error "${foo}_bar_baz"; # prints value of $foo and string _bar_baz "${foo_bar}_baz"; # prints value of $foo_bar and string _baz
我们需要这个,这样我们就不会在包
中获取pkg
.$a
的值我们将词法
时,这类似于将 sub 导入您的命名空间$a
的引用分配给类型 glob 中从 1 开始的那个槽。这将是一个标量引用。本质上,我们的变量$a
现在有了另一个名称$pkg::a
。当您说use Foo 'bar'
.
看完之后,我们可以更新您的代码。
sub split_at(&@) {
my $cb = shift;
my @input = @_;
my @retval = [];
my $pkg = caller;
my $max = $#input;
for my $i (0 .. $max) {
push @{$retval[$#retval]}, $input[$i];
no strict 'refs';
local *{"${pkg}::a"} = $input[$i];
local *{"${pkg}::b"} = $input[$i + 1]; # autovivification!
if (($i < $max) && $cb->()) {
push @retval, [];
}
}
pop @retval unless @{$retval[$#retval]};
return @retval;
}
my @files = map { { foo => $_ } } qw/ a a b b c d /;
my @grouped = split_at {
$a->{foo} ne
$b->{foo}
} @files;
我已经为我们的示例简化了数据结构。
对于循环的每次迭代,我们都需要 $a
和 $b
。因为通过类型 glob 将下一个元素分配给我们正在查看的 $b
会导致自动生成,所以我不得不将循环类型更改为计数器并引入一个新变量 $max
。我在构建它时 运行 进入了一个无限循环,因为它一直在 @input
.
undef
个元素
除此之外,代码几乎相同。我们不再需要向回调传递参数,我们需要 no strict 'refs'
。您通常会为尽可能小的范围关闭 strict
。我们还需要获取 caller
的命名空间,以便我们可以将变量放在那里。
您还需要注意一件事。 List::Util::PP sets up $a
和 $b
在调用者的命名空间中,否则我们可能会导致警告,因此如果您想将此函数放入库中,您应该使用相同的代码。
sub import { my $pkg = caller; # (RT88848) Touch the caller's $a and $b, to avoid the warning of # Name "main::a" used only once: possible typo" warning no strict 'refs'; ${"${pkg}::a"} = ${"${pkg}::a"}; ${"${pkg}::b"} = ${"${pkg}::b"}; goto &Exporter::import; }