Perl 添加MM::SS次,将其除以并打印平均值
比方说我刚刚跑了10公里,现在我有10公里的分割时间,以MM::SS的形式。我想要一个简单的方法,将任意数量的分割时间相加并求平均值。例如,也许我想看看最后5公里比前5公里快(或慢)多少 我可以自己解析时间,将它们划分为秒,然后将它们转换回MM::SS。数学并不难,但我想知道CPAN上的某些东西是否已经以一种简单的方式做到了这一点。我的第一次尝试是使用DateTime,但由于闰秒的原因,它不能从秒转换为分钟 明确地说,在这种情况下,我并不关心闰秒,但我很好奇库是否存在。举个例子,下面是我尝试过的Perl 添加MM::SS次,将其除以并打印平均值,perl,datetime,Perl,Datetime,比方说我刚刚跑了10公里,现在我有10公里的分割时间,以MM::SS的形式。我想要一个简单的方法,将任意数量的分割时间相加并求平均值。例如,也许我想看看最后5公里比前5公里快(或慢)多少 我可以自己解析时间,将它们划分为秒,然后将它们转换回MM::SS。数学并不难,但我想知道CPAN上的某些东西是否已经以一种简单的方式做到了这一点。我的第一次尝试是使用DateTime,但由于闰秒的原因,它不能从秒转换为分钟 明确地说,在这种情况下,我并不关心闰秒,但我很好奇库是否存在。举个例子,下面是我尝试过的
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw( say );
use DateTime::Format::Duration;
my $formatter = DateTime::Format::Duration->new( pattern => '%M:%S' );
my @splits = @ARGV;
my $split_number = @splits;
my $total = $formatter->parse_duration( shift @splits );
foreach my $split (@splits) {
$total->add_duration(
$formatter->parse_duration($split) );
}
say 'Total time: ' . join ':', $total->minutes, $total->seconds;
$total->multiply( 1 / $split_number );
say 'Average time: ' . join ':', $total->minutes, $total->seconds;
say 'Using DateTime::Format::Duration: ' . $formatter->format_duration( $total );
和一个示例运行:
$ perl script/add-splits.pl 1:30 2:30
Total time: 3:60
Average time: 1.5:30
Using DateTime::Format::Duration: 01:30
所以,你可以看到duration对象本身,给了我一个正确的答案,但不是人类想要破译的东西
DateTime::Format::Duration试图提供帮助,但在这个过程中花费了30秒
我不是在寻找做这件事的原始代码。我感兴趣的是CPAN上是否已经存在这种情况。这里有一个使用
DateTime
对象的版本。由于它不处理日期解析,我们必须自己拆分字符串
添加拆分.pl
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw( say );
use DateTime;
my @splits = @ARGV;
my $split_number = @splits;
my $fmt = "%M:%S";
my $total = DateTime->from_epoch( epoch => 0 );
foreach my $split (@splits) {
my ($split_min, $split_sec) = split /:/, $split;
$total->add( minutes => $split_min, seconds => $split_sec );
}
say 'Total time: ' . $total->strftime($fmt);
my $avg_seconds = $total->epoch / $split_number;
my $avg = DateTime->from_epoch( epoch => 0 )->add( seconds => $avg_seconds );
say 'Average time: ' . $avg->strftime($fmt);
输出
$ perl add_splits.pl 1:30 2:30
Total time: 04:00
Average time: 02:00
在CPAN上查找这一确切功能的问题在于,您希望操作具有时间间隔的字符串。大多数模块都与这些字符串的上下文有关,将它们作为日期和时间使用。因此很难找到简单地添加
mm:ss
格式的东西。既然这个任务相当具体,而且编写起来也很简单,为什么不把它包装在您自己的包中呢
话虽如此,看看下面的片段是否符合您的要求
这是一个具有核心模块的简单解决方案。它确实需要几秒钟来计算,但是它可以被包装在几个sub中,然后也可以轻松地扩展到其他计算中
use warnings 'all';
use strict;
use feature 'say';
use Time::Piece;
use POSIX 'strftime';
use List::Util 'sum';
my @times = @ARGV;
my $fmt = '%M:%S';
my $tot_sec = sum map { Time::Piece->strptime($_, $fmt)->epoch } @times;
my $ave_sec = sprintf("%.0f", $tot_sec/@times); # round the average
my ($tot, $ave) = map { strftime $fmt, gmtime $_ } ($tot_sec, $ave_sec);
say "Total time: $tot";
say "Average time: $ave";
对于manip_times.pl 2:30 1:30
此打印
Total time: 04:00
Average time: 02:00
总时间:04:00
平均时间:02:00
我们使用Time::Piece
中的strtime
来获取对象,然后其epoch
方法返回秒数,这些秒数相加并求平均值。使用来自的strftime
将其转换回mm:ss
。Time::Piece
也有strftime
,但要使用它,我们必须有一个对象
请注意,Time::Piece
确实直接减去其对象,$t1-$t2
,但它无法添加它们。但是,可以向对象添加整数(秒),$t1+120
。也可以看到核心,这是发行版的一部分
对问题所用方法的评论 使用的对象无法在不同的单位之间转换 有关更多详细信息,请参阅DateTime.pm文档的一节。短期课程:一般来说,人们无法在秒、分、日和月之间进行转换,因此本课程永远不会这样做 再往下看一点,我们可以看到可以进行哪些转换,相关的转换只有“小时-分钟”和“秒-纳秒”。原因与闰秒、DST等有关。因此,计算必须产生
1.5
min等结果
请注意,这看起来也适用于这些特殊要求。DateTime::Format::Duration将秒累积为分钟的方式可能存在错误……您可能希望提交错误通知单。@xxfelixxx,谢谢。我和DateTime的作者聊过,他建议实际上不支持非整数单位的乘法,所以我对DateTime::Duration@oalders提出了一个bug,你能解释一下吗?我看到的问题不是乘法本身,而是分钟不能转换成秒,反之亦然。因此
3(min)/2
必须返回1.5
。他们说,在文档中,DateTime
和::Duration
(我的答案中有这个解释的链接)。我对这个问题产生了兴趣,搜索了我知道或能找到的所有日期/时间模块,但没有找到任何可以正确计算mm:ss的sum/ave的模块。(你的1:30
+2:30
对两者都是一个很好的测试用例。)@zdim我会跟他联系并在这里更新。@zdim我现在理解的问题是,在某些情况下,乘以整数会起作用,但不是在所有情况下。例如,如果最后的秒数为非整数,则不会转换为纳秒。所以,在某个时刻,很有可能得到一个坏值。@oalders我更新了我的答案,添加了更多的注释。这两个答案都非常有用。我接受了第一个,因为它是中的第一个,但这也帮助回答了我的问题:是否有一个现有的CPAN模块已经简单地实现了这一点。