Почему метод `later` приводит к бесконечному диапазону в диапазоне` Date`? - PullRequest
4 голосов
/ 04 августа 2020

В Julia вы можете сгенерировать диапазон дат по месяцам следующим образом:

julia> dr = Date(2014,1,29):Dates.Month(1):Date(2014,07,29)
Date("2014-01-29"):Month(1):Date("2014-07-29")

julia> collect(dr)
7-element Array{Date,1}:
 2014-01-29
 2014-02-28
 2014-03-29
 2014-04-29
 2014-05-29
 2014-06-29
 2014-07-29

Date(2014,1,29) - дата начала, Dates.Month(1) - шаг, Date(2014,07,29) - дата окончания.

У Раку есть метод позже , но при использовании в настраиваемом генераторе он приводит к бесконечному диапазону:

lazy my @dates = Date.new('2014-01-29'), Date.new('2014-02-28'), { $^a.later(:1month) } ... Date.new('2014-07-29')

Если я использую * >= Date.new('2014-07-29') вместо Date.new('2014-07-29') справа от оператора ... работает:

lazy my @a = Date.new('2014-01-29'), Date.new('2014-02-28'), { $^a.later(:1month) } ... * >= Date.new('2014-07-29')


2014-01-29
2014-02-28
2014-03-28
2014-04-28
2014-05-28
2014-06-28
2014-07-28
2014-08-28

почему пользовательский генератор { $^a.later(:1month) } в { $^a.later(:1month) } ... Date.new('2014-07-29') не останавливается на 2014-07-29 и привести к бесконечному диапазону?

Ответы [ 2 ]

5 голосов
/ 04 августа 2020

Как указывает JJMerelo в комментариях, способ, которым работает оператор ..., заключается в продолжении генерации элементов на основе левых аргументов до тех пор, пока не будет точно достигнут правый аргумент (для каждого смарт-сопоставления).

Например, если мы создали последовательность, кратную 10,

my @tens = 0, 10, 20 ... 95;
say @tens[10]; # 100
say @tens[11]; # 110

Это потому, что не элемент @tens на самом деле будет 95. Чтобы определить, является ли элемент последним, Оператор smartmatch (~~) используется. Смарт-сопоставление DateTime с другим Datetime возвращает истину, если эти два представляют одно и то же время (которое может быть разным из-за часовых поясов, и т. Д. c).

Для последовательностей DateTime дополнительно осложняется тем, что .later и .earlier не являются общими, поэтому выполнение $date.later(:1month).later(:1month) не гарантирует того же результата, что и $date.later(:2month).

Причина, по которой * ≥ DateTime.new(…) отличается, заключается в том, что интеллектуальное сопоставление для Callable объектов (что технически так и есть, любой код, эквивалентный anon sub $dt { $dt ≥ DateTime.new(…) }, передает левый аргумент вызываемому объекту. Если вы не уверены на 100%, что последовательность завершится при достижении точного значения, лучше всего использовать подход с любым кодом для обеспечения совпадения значения.

2 голосов
/ 06 августа 2020

Хммм - для любых детей, пытающихся это сделать, есть некоторые подводные камни, связанные с тем, «что я имею в виду» ... например, когда я пробую это

lazy my @b = Date.new('2014-01-31'), Date.new('2014-02-28'), { $^a.later(:1month) } ... * >= Date.new('2014-07-29')

Я получаю это ...

#(2014-01-31 2014-02-28 2014-03-28 ...)

Но, может быть, мне нужен последний день каждого месяца, что, если 2014 - високосный год, бла, бла

Итак, если вы хотите последний день (или последний день -1) , то есть еще такой удобный способ ...

say Date.new('2015-11-24').last-date-in-month;
...