Perl 添加MM::SS次,将其除以并打印平均值

Perl 添加MM::SS次,将其除以并打印平均值,perl,datetime,Perl,Datetime,比方说我刚刚跑了10公里,现在我有10公里的分割时间,以MM::SS的形式。我想要一个简单的方法,将任意数量的分割时间相加并求平均值。例如,也许我想看看最后5公里比前5公里快(或慢)多少 我可以自己解析时间,将它们划分为秒,然后将它们转换回MM::SS。数学并不难,但我想知道CPAN上的某些东西是否已经以一种简单的方式做到了这一点。我的第一次尝试是使用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模块已经简单地实现了这一点。