2017-02-12 4 views
2

Я хочу создать последовательность, которая возвращает в результате все годы, когда день недели будет таким же, как и в функциональном параметре (например: все годы, когда 12 февраля - воскресенье, так как Дата начала).Последовательность - та же самая дата в последующие годы

let myDate (dw:System.DayOfWeek) (start:System.DateTime) = 
    seq { 
      ... 
     } 

Надеюсь, вы поняли, что я имею в виду.

+6

Мы понимаем, что вы имеете в виду, но что вы пробовали и что вы сочтете сложным? Это не служба написания кода ... – scrwtp

ответ

4

Как об этом: «да, я бы удалить некоторые из этого кода»

let myDate (dw:System.DayOfWeek) (start:System.DateTime) = 
    Seq.initInfinite ((+)1) // from 1..∞ 
    |> Seq.map start.AddYears 
    |> Seq.takeWhile (fun x -> x < DateTime.MaxValue) 
    |> Seq.filter (fun x -> x.DayOfWeek = dw) 
    |> Seq.map (fun x -> x.Year) 

myDate System.DayOfWeek.Monday (new DateTime(2001,1,1)) |> Dump 

или

let myDate (dw:System.DayOfWeek) (start:System.DateTime) = 
    let rec inner i = 
     seq { 
      let someDay = start.AddYears(i) 
      if someDay.DayOfWeek = dw then yield someDay.Year 
      yield! inner (i+1) 
     } 
    inner 1 

myDate System.DayOfWeek.Monday (new DateTime(2001,12,1)) |> Dump 
+2

'(id >> (+) 1)' это забавный способ сказать '((+) 1)'. ; -] – ildjarn

+1

О да, я обвиняю его в отсутствии кофе :-D – Stuart

+0

Обратите внимание, что високосные годы - это угловой футляр, с которым вам придется иметь дело. Вызов «AddYears (6)» 29 февраля 2012 года (среда) выйдет 28 февраля 2018 года (это также будет среда). Таким образом, вы получите ложные срабатывания с этим подходом, если вы также не убедитесь, что дата календаря не изменилась при вызове 'AddYears (i)'. См. Документацию ['AddYears'] (https://msdn.microsoft.com/en-us/library/system.datetime.addyears (v = vs.110) .aspx) для получения подробной информации о том, как она обрабатывается 29 февраля. – rmunn

1

ответ Стюарта это хорошо, но я не могу не думать Вы знаете, начинайте с нескончаемой последовательности лет с этим конкретным месяцем/днем ​​и позвольте собеседнику определить, сколько взять или добавить собственный фильтр.

На этом этапе вы должны думать об «безумном поручении». :) Первая проблема, с которой я столкнулся - потому что мой второй неудачный тест не удался - было то, что вы не можете весело наращивать DateTime навсегда, поэтому вам нужно написать код, чтобы ограничить это.

Так что я в конечном итоге с гораздо более кода, чем меньше, из-за борьбы с капризами реальных данных:

// All instances of this specified month/day beginning with 'start'. 
let myDate (dw:System.DayOfWeek) (start:System.DateTime) = 
    // Start with a sequence of one date for each year... 
    seq { 
     // Don't run the date past DateTime.MaxValue, there lurks an 
     // ArgumentOutOfRangeException. Comparison also must be on the 
     // valid side of DateTime.MaxValue. Don't bother with try/with 
     // here in a sequence expression, you can't do that. 
     let maxDateBarrier = DateTime.MaxValue.AddYears(-1) 
     let mutable keepGoing, date = true, start 

     while keepGoing do 
      yield date 

      // if date.Year % 100 = 0 then printfn "%A" date.Year 

      if date <= maxDateBarrier then 
       date <- date.AddYears(1) 
      else 
       keepGoing <- false 
    } 
    |> Seq.where (fun date -> date.DayOfWeek = dw) 

Немного многословным. Тест # 1 работает отлично:

let printDates (dates : DateTime seq) = 
    for date in dates do 
     printfn "%A" date.Year 

// Take the next 5 
myDate DayOfWeek.Sunday DateTime.Now 
    |> Seq.take 5 
    |> printDates 

Тест # 2 фактически перебирает все из дат в моей последовательности:

// Take up to 5 before 2040. 
// Note: this statement actually iterates through *all* the years in the 
// sequence if you truncate to a length longer than Seq.where returns. 
myDate DayOfWeek.Sunday (DateTime(2017, 2, 13)) 
    |> Seq.where (fun date -> date.Year < 2040) 
    |> Seq.truncate 5 
    |> printDates 

Является Тест # 2 причина, чтобы добавить endDate функции myDate ? Нет, это скорее аргумент, чтобы лучше использовать последовательности. Используйте takeWhile вместо where:

// Try again: Take up to 5 before 2040. 
// Terminate the sequence early with takeWhile. 
myDate DayOfWeek.Sunday (DateTime(2017, 2, 13)) 
    |> Seq.takeWhile (fun date -> date.Year < 2040) 
    |> Seq.truncate 5 
    |> printDates 

Намного лучше. Пропустил мою цель «меньше кода», но я в порядке с этим. :)

+0

Это * больше * код (а не тот, что меньше кода должен быть целью) и добавляет изменчивость. Но всегда приятно видеть другие ответы =) – Stuart

+0

Я бы сказал, что «меньше кода» должно * часто быть целью, потому что это обычно помогает читать. (Конечно, это не применимо, если более короткий код - это беспорядок, похожий на Perl-one-liner). Антуан де Сент-Экзюпери однажды сказал: «Совершенство достигается, когда нет ничего более, чтобы добавить, но когда ничего не осталось отнять », и он был совершенно прав. Обычно, когда вы оцениваете свой дизайн (или ваш код) до самого необходимого, результат становится более элегантным и легче читать. Таким образом, я обычно стараюсь «меньше кода» как цель дизайна большого пальца. – rmunn

+0

@ Стюарт, мне показалось, что я ясно дал понять, что «пропустил свою цель« меньше кода ». :) Что мне было интересно, это граничные случаи, которые возникли, нескончаемые последовательности, которые заканчиваются, и неправильное использование операций последовательности , Имейте в виду, я не достаточно серьезно отношусь к этому, чтобы справиться с високосными годами, это было какое-то обеденное время. :) –

1

Я слишком ленив, чтобы закодировать это, но я бы пошел по этому пути, чтобы найти эффективный и быстрый алгоритм, который учитывает високосные годы. Это интересная задача, я думаю, и было бы интересно сравнить скорость с другими ответами.

Есть 14 возможных календарей. Как повторяются эти календари, вы можете быстро узнать, если вы это сделаете. Я нашел это.

«Григорианский календарь повторяется в 28-летних циклах. Календарь для определенного непикового года повторяется через 11 лет дважды и повторяется через шесть лет один раз в 28-летнем периоде. Календари для високосных годов повторяются себя каждые 28 лет ».

Как повторяется конкретный день в году, я думаю, может быть немного сложнее, но если так, то не много.

Любой из семи календарей без скачка, конечно, будет соответствовать одному из календарей високосного года с января по февраль, кроме 29 февраля.Кажется очевидным, поначалу считалось, что также будет матч с марта, за исключением того, что он будет искажен одним днем ​​и одним календарем по сравнению с янв. Feb28. Требуется проверка. Мне интересно, повлияет ли это на алгоритм, и если да, то каким образом.

Если ваш ввод 29 февраля, то, очевидно, нужно учитывать только високосные годы.

Из этого достаточно написать алгоритм.

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

Смежные вопросы