如何简化包含“int”、模数和除法的表达式?

3
下面的脚本计算时间差的十进制数,但我得到了一个舍入误差,所以我猜这是因为我在最后的转换非常糟糕。
问题:有些东西告诉我,表达式可以简化,但在这种特定情况下,当我有int、模数和除法时,数学规则是什么?
#!/usr/bin/perl

print f("9:00", "16:45");

sub f {
    my $t1 = shift @_;
    my $t2 = shift @_;

    my $m1 = $1*60+$2 if $t1 =~ m/(\d\d?):(\d\d?)/;
    my $m2 = $1*60+$2 if $t2 =~ m/(\d\d?):(\d\d?)/;

    my $h = int(($m2-$m1)/60);

    return $h + ($h*60+$m2-$m1)%60/60;
}

1
你期望得到什么结果? - ikegami
2
你不应该自己编写时间处理代码。当time1为凌晨1:20,time2为凌晨3:20,日期为3/9时会发生什么?实际的时间差是1小时。可以查看时间模块,例如DateTime套件:http://search.cpan.org/search?query=DateTime - Oesor
实际的时间差是1小时 - 这取决于你在哪个国家。并非所有国家都有夏令时,而且采用夏令时的国家也不会在同一日期更改(我不知道有哪个国家会在9月3日更改)。 - Dave Cross
@DaveCross 我相信Oesor是指3月9日。在另一篇帖子中读到你的一条评论,关于“被那些认为MM/DD/YY是日期可接受格式的人烧伤过多次”,我觉得这很有趣 :) - ThisSuitIsBlackNot
哦,我知道他的意思。我想表达的微妙之处在于,在像SO这样的国际论坛上,日期格式为M/D(或者D/M)对大多数读者来说只会造成困惑。 - Dave Cross
3个回答

6
你可以使用Time::Piece来进行简单的日期/时间计算:
#!/usr/bin/perl

use strict;
use warnings;

use Time::Piece;

my $pattern = '%H:%M';

my $start = Time::Piece->strptime('09:00', $pattern);
my $end   = Time::Piece->strptime('16:45', $pattern);

my $diff  = $end - $start;
print $diff->hours;

输出:

7.75

请注意,$diff 实际上是一个 Time::Seconds 对象。

2
我假设OP是在做某种练习,但如果不是的话,这个答案绝对是生产使用的更好解决方案。尽可能避免重新发明轮子,特别是涉及日期/时间计算的时候,因为这些主题非常棘手。 - TypeIA

6
您已经正确计算了$m1$m2,它们表示自午夜以来经过的分钟数。因此,为什么不直接将时间差作为小数小时返回:
return ($m2 - $m1) / 60.0;

就“数学规则”而言,也许看一下你的return和我的有什么区别会有所帮助(忽略四舍五入):

$h + ($h * 60 + $m2 - $m1) % 60 / 60

请注意:($h * 60) % 60 中午的值为零,因此这一项基本上会消失,留下以下内容:
$h + ($m2 - $m1) % 60 / 60

现在考虑一下如何计算 $h 的:它是 ($m2 - $m1) 除以 60 的商,去掉余数(因为使用了 int())。另一方面,($m2 - $m1) % 60 正是这个除法的余数。因此,上述表达式基本上只是将你从 $h 中去掉的余数部分加回来而已。因此,它与以下表达式的结果相同:
($m2 - $m1) / 60

那是一个非常好的观点。从我的复杂表达转换到你的表达是否可能?或者算法不能与方程式相比较吗? - Jasmine Lognnes
1
@JasmineLognnes,在我更新帖子并列出了一些步骤说明两者是等价的之前,您可能已经发表了评论?可以通过代数方法证明它们是等价的;关于“将余数加回去”的最后一步可以更严谨地书写,但我尝试传达了这个想法。 - TypeIA

1
我建议使用Time::Piece, 但我认为这是某种练习。
在Unix中,所有日期都转换为自“纪元”以来的秒数。当一切都以相同单位表示时,进行运算变得容易。因此,我会将时间转换为分钟,执行操作,然后将时间转换回小时和分钟。这意味着您需要两个子程序。一个将时间从小时和分钟转换为分钟。另一个将分钟转换为小时和分钟。
#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);

my $time1 = "9:50";
my $time2 = "16:45";

my $time1_in_minutes = convert_to_minutes( $time1 );
my $time2_in_minutes = convert_to_minutes( $time2 );

my $diff_in_minutes = $time2_in_minutes - $time1_in_minutes;

say convert_to_hours( $diff_in_minutes );


sub convert_to_minutes {
    my $time             = shift;

    my ($hours, $minutes) = split /:/, $time;
    my $total_minutes = ( $hours * 60 ) + $minutes;
    return $total_minutes;
}

sub convert_to_hours {
    my $minutes         = shift;

    my $hours = int $minutes / 60;
    $minutes = $minutes % 60;
    return sprintf "%d:%02d", $hours, $minutes;
}

通过将工作分解成两个子程序,您可以轻松地看到正在发生的事情。此外,您有更多的灵活性。如果我给了你我跑马拉松所用的时间,我想知道平均时间怎么办?您可以使用相同的两个例程。如果我告诉你我每天在工作中花费的时间,并且我想要一个总时间怎么办?同样,您可以使用相同的子例程。
而且,因为很容易看出发生了什么,所以在编程时出现错误时更容易纠正错误。更好的是,由于操作很干净,我可以开始添加功能。例如,检查我的输入会很好。在我的convert_to_minutes子例程中,我可能想确保小时和分钟是有效的。中间有一个冒号。分钟不大于60。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接