问题
I want to generate a sequence that ends last Friday from Monday to Thursday, and the Friday of the previous week if the sequence starts on Saturday and Sunday. That is, assuming that today is 2018-05-09
, then last Friday is 2018-05-04
,
If today is 2018-05-12
, then last Friday is also 2018-05-04
. So I write:
(Date.today, *.earlier(:1day) ... ( *.day-of-week==5 && *.week[1]+1==Date.today.week[1] )).tail # Output: 2018-05-06
But the result is 2018-05-06
instead of 2018-05-04
.
Then I used a Junction:
(Date.today, *.earlier(:1day) ... all( *.day-of-week==5, *.week[1]+1==Date.today.week[1] )).tail # Output: 2018-05-04
Why &&
in the first situation is wrong? The ... operator says that:
The right-hand side will have an endpoint, which can be Inf or * for "infinite" lists (whose elements are only produced on demand), an expression which will end the sequence when True, or other elements such as Junctions.
What's wrong with the &&
operator?
回答1:
The problem is your ending condition
*.day-of-week==5 && *.week[1]+1==Date.today.week[1]
That is two WhateverCode lambdas that each take 1 argument.
*.day-of-week==5
*.week[1]+1==Date.today.week[1]
Since a code object is a true value, the &&
operator moves onto the second one. So the sequence stops when it reaches sunday of the previous week.
Even if the code was a single lambda it wouldn't work like you expect as it would be a lambda that takes two arguments.
The right way to do this check is to use some sort of block.
{.day-of-week==5 && .week-number+1 == Date.today.week-number}
It might be a good idea to wrap it in a subroutine so that you can test it.
sub last-friday ( Date:D $date ) {
# cache it so that it doesn't have to be looked up on each iteration
my $week-number = $date.week-number - 1;
(
$date,
*.earlier( :1day )
...
{
.day-of-week == 5
&&
.week-number == $week-number
}
).tail
}
say last-friday Date.new: :year(2018), :month( 5), :day( 9); # 2018-05-04
say last-friday Date.new: :year(2018), :month( 5), :day(12); # 2018-05-04
say Date.today.&last-friday; # 2018-05-04
You could also just calculate the proper date.
sub last-friday ( Date:D $date ) {
$date.earlier:
days => (
$date.day-of-week # reset to previous sunday
+ 2 # go two days earlier to get friday
)
}
say last-friday Date.new: :year(2018), :month( 5), :day( 9); # 2018-05-04
say last-friday Date.new: :year(2018), :month( 5), :day(12); # 2018-05-04
say Date.today.&last-friday; # 2018-05-04
回答2:
This sequence will go from today to last Friday:
say Date.today, *.earlier(:1day) ... *.day-of-week==5
# OUTPUT: «(2018-05-09 2018-05-08 2018-05-07 2018-05-06 2018-05-05 2018-05-04)»
Also
say Date.new("2018-05-11"), *.earlier(:1day) ... *.day-of-week==5;
# OUTPUT: «(2018-05-11)»
The last argument of the ... operator is the sequence terminator, or a condition it must meet. If the sequence must end on Friday, the simplest condition is that one above.
来源:https://stackoverflow.com/questions/50245452/find-last-friday-s-date-in-perl-6