在我的测试中,我想添加一个函数,它可以对一段代码进行计时。这段代码就像一个Map,你可以在大括号中给予它。我使用了这个answer,它运行得很好。
Timer.pm
sub recordTime(&@)
{
# https://stackoverflow.com/a/6101734/11365539
my $code = \&{shift @_};
my %opts = @_;
$opts{name} //= 'Time';
$opts{fmt} //= 'default';
$opts{hires} //= 0;
my $timing_sub = $opts{hires} ? \&CORE::time : \&Time::HiRes::time;
my $start = $timing_sub->();
$code->();
my $end = $timing_sub->();
my $output;
if($opts{log})
{
$output = IO::File->new($opts{log}, '>>')
or carp("Failed to open $opts{log}. Outputting to STDOUT...");
}
if(not defined($output)) #if $opts{log} is undefined or IO::File->new() failed
{
$output = *STDOUT;
}
my $time_diff = $end - $start;
$output->print("$opts{name}: ");
# formatting time and printing it
{
no warnings 'numeric'; # perl complains that *STDOUT is not numeric
if($output != *STDOUT)
{
$output->close();
}
}
}
1;
这段代码看起来像预期的那样工作。我可以这样使用它:
Timer::recordTime {
runTests();
} (name => 'Run all tests', log => $timing_log);
我不想到处写log => $timing_log
,所以我在另一个模块中添加了这个函数。
TesterInterface.pm
sub logTime(&@)
{
my $code = \&{shift @_};
my %opts = @_;
Timer::recordTime {
$code->();
} (%opts, log => $timing_log);
}
所以我会这样使用它
TesterInterface::logTime {
runTests();
} (name => "Run all tests");
这样会不断抛出Undefined subroutine &main::0 called
之类的错误。在我看来,这似乎是在计算$code
最后一行的隐式返回。我通过在代码块末尾添加say("hi")
语句来测试这一点,该语句将返回1。然后我看到了Undefined subroutine &main::1 called
。我试图通过将该行更改为\ say("hi")
来避开它,但这产生了以下错误:Can't call method "logTime" on unblessed reference
.我真的不知道如何修复这个问题。
我确实发现这样做是可行的,但我更愿意避免编写sub
。我也不明白为什么这个方法可行,但代码块版本不行。
TesterInterface::logTime(sub {
runTests();
}, name => 'Run all tests');
我在Windows中使用Perl v5.22.1
1条答案
按热度按时间w41d8nur1#
您没有提供问题的演示,但很明显,对
Timer::recordTime
的调用发生在编译Timer::recordTime
的声明之前。这会导致
被解析为等效于以下内容的间接方法调用:
对了,
是一种复杂的方式
前者除了sub ref之外还接受sub name,但原型不允许这样做。
顺便说一下,您的
logTime
增加了很多不必要的开销用途:&
会导致忽略原型。