🎄 13/25. 本世纪多少天符合条件?

欢迎来到 Raku One-Liner Advent Calendar 的第13天!今天的单行将很长,最好用两行编写,但它会显示 Raku 的 Date 对象的一个​​非常好的功能:它可以很容易地在一个范围内使用。

今天,我们正在解决欧拉项目的问题19。从本质上讲,任务是计算在1901年1月1日至2000年12月31日期间的星期日,这是在第一个月。

Raku 中的 Date 对象实现了 succprec 方法,它们递增和递减日期。也可以使用两个日期作为范围的边界:

1
2
3
say (
Date.new(year => 1901) ..^ Date.new(year => 2001)
).grep({.day == 1 && .day-of-week == 7}).elems

在这里评论一下。

首先,使用单个命名参数(year)创建两个 Date 对象。这是可能的,因为构造函数的签名具有月和日的默认值:

1
2
3
4
5
6
multi method new(
Date: Int:D() :$year!,
Int:D() :$month = 1, Int:D() :$day = 1,
:&formatter, *%_) {
. . .
}

因此,创建1月1日的日期很容易,但是在一年的最后一天你不能这样做。但是 Raku 有一个很好的范围运算符 ..^,它排除了右边界并允许我们节省相当多的字符(而我们还没有玩 Raku Golf :-)。

具有日期的所有显式部分的较长版本将如下所示:

1
2
3
4
say (
Date.new(year => 1901, month => 1, day => 1) ..
Date.new(year => 2000, month => 12, day => 31)
).grep({.day == 1 && .day-of-week == 7}).elems

您可以使用组合条件创建范围并 grep 其值。请记住,当您要在默认变量上调用方法时,无需显式键入 $_

另一种方法是使用带有星号的两个 greps:

1
2
3
4
say (
Date.new(year => 1901, month => 1, day => 1) ..
Date.new(year => 2000, month => 12, day => 31)
).grep(*.day == 1).grep(*.day-of-week == 7).elems

给你留个家庭作业:打印此 Advent 日历结束前剩余的天数。明天见!