2

Например, я хочу получить даты каждой пятницы с этого момента до 30 дней.Получение каждой пятницы до определенной даты, но в функциональном стиле?

В настоящее время я могу использовать библиотеку underscore и moment.js для этого. Но результат является сверхвероятным и досадно процедурным/императивным. Обратите внимание:

var initDate = moment().day("Friday"); 
var endDate = moment().add(30, 'days'); 

var result = []; 
result.push(initDate); 

while (_.last(result).isBefore(endDate)) { 
    var x = _.last(result); 
    result.push(nextWeek(x)); 
} 

alert(result); // answer here 

// create a new moment from given moment and add 7 days 
function nextWeek(initMoment) { 
    var x = moment(initMoment); 
    return x.add(7,'days');  
} 

Вот соответствующая скрипку: http://jsfiddle.net/aafsh5xa/

мне было интересно, если есть способ использовать Haskell-подобные функции, такие как списковые или бесконечные списки (ленива оценкой), чтобы сделать это намного больше кратким. Может быть что-то вроде:

var initDate = moment(); 
var endDate = moment().add(30,days); 

var everyFriday = genLazyList(initDate, nextFridayFrom); 
var result = _.filter(everyFriday, function(input){ return input.isBefore(endDate); }); 

Обратите внимание, что everyFriday бесконечный список генерируется genLazyList и не оценена, пока функция _.filter() не вызывается на него. А nextFridayFrom() - это функция, которую genLazyList использует для создания ленивого списка.

+1

может быть, это только мне, но не это должно быть довольно просто -> ** Http: //jsfiddle.net/aafsh5xa/1/** – adeneo

+0

Я спрашиваю конкретно об использовании методов функционального программирования для его выполнения. Ваша скрипка и моя скрипка так же нефункциональны. – adrianmc

+0

Короче говоря, нет никакого способа сделать это в JS, «genLazyList» будет выполняться и оцениваться при вызове и возвращаться каждую пятницу с этого момента до вечности, нет возможности отложить его оценку до тех пор, пока он не пройдет через фильтр. – adeneo

ответ

1

Примечание Я использую stream.js и moment.js в моем ответе.Таким образом, вы должны будете сделать следующее включает в вашем HTML (обратите внимание, что мой синтаксис в нефрит):

script(src="path/to/stream.js") 
script(src="path/to/moment.js") 

А вот мой код:

var initDate = moment().day("Friday"); 
var endDate = moment().add(30,'days'); 

function allFridays() { 
    return new Stream(initDate, function(){ 
      return allFridays().map(function(date){return moment(date).add(7,'days')}); 
     } 
    );  
} 

allFridays().takeWhile(function(h){return h.isBefore(endDate)}).print(); 

Или в мой любимый аромат, CoffeeScript :

initDate = moment().day('Friday') 
endDate = moment().add(30, 'days') 

allFridays = -> 
    new Stream(initDate, -> 
    allFridays().map (date) -> 
     moment(date).add 7, 'days' 
) 

allFridays().takeWhile((h) -> h.isBefore endDate).print() 

Не так ли просто красиво?

Быстрое объяснение:

  1. Я делаю бесконечный поток момента (дата) объекты, которые не представляют все пятницы теперь, пока навсегда
  2. я беру (т.е. извлечения) элементы этого потока вверх до тех пор, первый момент, который не представляет времени до моего endDate.

И все!

Обратите внимание, что функция takeWhile() еще не реализована в официальной версии stream.js, но я просто скопировал ее и вставлял ее из папки lib. См. Его git repo here.

+1

Уход. Вот мой подход к нему (используя stream.js), без 'moment.js' и generic для всех рабочих дней: http://jsfiddle.net/KooiInc/0L02La7v/ – KooiInc

+0

приятно! Некоторые полезные идеи есть, @KooiInc. – adrianmc

1

Мне было интересно, есть ли способ использовать подобные Haskell функции, такие как списки или бесконечные списки (ленивая оценка), чтобы сделать это намного более кратким.

В ES6, вы будете иметь возможность использовать функцию генератора (spec, MDN) для бесконечного списка.

В то же время, петли в функциональном программировании обычно выполняются посредством рекурсии, правильно? Итак:

function getFridays(f, dt, days) { 
 
    if (days > 0) { 
 
    if (dt.getDay() === 5) { 
 
     f.push(new Date(dt)); 
 
    } 
 
    dt.setDate(dt.getDate() + 1); 
 
    f = getFridays(f, dt, days - 1); 
 
    } 
 
    return f; 
 
} 
 

 
var fridays = getFridays([], new Date(), 30); 
 
document.body.innerHTML = '<pre>' + JSON.stringify(fridays, null, 4) + '</pre>';

Эта версия имеет побочные эффекты, хотя (два из ее аргументов являются объектами, состояние которых мутируют в функции). Я не очень хорошо разбираюсь в функциональном программировании, но я понимаю, побочные эффекты, следует избегать, так что, возможно:

function getFridays(date, days) { 
 
    var f, newDate; 
 
    
 
    f = []; 
 
    if (days > 0) { 
 
    if (date.getDay() === 5) { 
 
     f.push(new Date(date)); 
 
    } 
 
    newDate = new Date(date); 
 
    newDate.setDate(newDate.getDate() + 1); 
 
    return f.concat(getFridays(newDate, days - 1)); 
 
    } 
 
    return f; 
 
} 
 

 
var fridays = getFridays(new Date(), 30); 
 
document.body.innerHTML = '<pre>' + JSON.stringify(fridays, null, 4) + '</pre>';

Это, очевидно, намного меньше памяти эффективным (с точки зрения GC churn), но если я правильно понимаю, отсутствие побочных эффектов более важно в FP, чем эффективность памяти/GC.

+0

@AlexeyRomanov: Да, хорошая настройка. –

+0

Просто отметить ... Вы можете мутировать состояние даже на языке, таком как Haskell. Важным соображением является то, является ли состояние, мутированное, невидимым вне сферы действия, тем самым сохраняя ссылочную прозрачность. См. Этот ответ о ST Monad, который позволяет нам мутировать состояние, статически гарантируя это свойство. http://stackoverflow.com/a/8197275/536017 – danem

+0

@ danem: Правильно. Вот почему я опубликовал второй пример: мутации, вызванные первым примером, будут отображаться вне функции, например, побочных эффектов. –

2

Имеет ли JS юлианскую библиотеку даты? Это упростит ситуацию.

Вот некоторые псевдо-Haskell код при условии, что мы имеем следующие функции:

toJulianDate :: Date -> Int 
toDate :: Int -> Date 
dayOfWeek :: Date -> Int  -- 0 = Sun, 1 = Mon, ... 5 = Fri, etc. 

Тогда мы можем написать:

everyFridayBetween :: Date -> Date -> [Date] 
everyDridayBetween d1 d2 = map toDate [jfriday, jfriday+7..j2] 
    where 
    j1 = toJulianDate d1 
    j2 = toJulianDate d2 
    dow = dayOfWeek d1 
    jfriday = if dow <= 5 then j1+(5-dow) else j1 + 7 + (5-dow) 
+0

Это довольно интересный способ сделать это! – adrianmc

0

Некоторые доказательство концепции с ES6 генератора функциональность

function *fridays13th() { 
 
    var nextFriday = getNextFriday(new Date()) 
 

 
    while (true) { 
 
    if (isFriday13th(nextFriday)) { 
 
     yield new Date(nextFriday) 
 
    } 
 

 
    nextFriday = getNextFriday(nextFriday) 
 
    } 
 

 
    function isFriday13th(day) { 
 
    return ((day.getDay() === 5) && (day.getDate() === 13)) 
 
    } 
 

 
    function getNextFriday(d) { 
 
    return new Date(d.setDate(d.getDate() + (5 - d.getDay() > 0 ? 5 - d.getDay() : 12 - d.getDay()))) 
 
    } 
 
} 
 

 

 
function generator(fn) { 
 
    var res = [] 
 
    var g = fn() 
 

 
    return { 
 
    take: function(max) { 
 
     for (var i = 0; i < max; i += 1) { 
 
     var r = g.next() 
 
     res.push(r.value) 
 
     } 
 
     return res 
 
    } 
 
    } 
 
} 
 

 

 
var fridays = generator(fridays13th).take(5) 
 

 

 
document.body.innerHTML = '<pre>' + JSON.stringify(fridays, null, 4) + '</pre>';

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