每周五到某个日期,但功能性强?
Getting every Friday until a certain date, but in a functional style?
比如我想获取从现在到30天后每个星期五的日期。
目前,我可以利用下划线库和 moment.js 来做到这一点。但结果是超级冗长和烦人 procedural/imperative。观察:
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');
}
这里是对应的fiddle: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 用来制作惰性列表的函数。
I was wondering if there's a way to use Haskell-like features such as list comprehensions or infinite lists (lazy evaluation) to make this a lot more concise.
在 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 流失而言),但如果我理解正确的话,在 FP 中没有副作用比 memory/GC 效率更重要。
JS 有 Julian 日期库吗?这样事情就简单多了。
这里是一些伪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)
请注意,我在回答中使用了 stream.js and moment.js。因此,您必须在 HTML 中执行以下操作(请注意,我的语法是用 jade 编写的):
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()
是不是很漂亮?
快速解释:
- 我制作了一个无限的时刻(日期)对象流,代表从现在到永远的所有星期五
- 我获取(即检索)此流的元素直到第一时刻,这并不代表我的结束日期之前的时间。
就是这样!
请注意,takeWhile()
功能尚未在官方 stream.js 版本中实现,但我只是从其 lib 文件夹中复制并粘贴它。请参阅其 git 回购 here。
具有 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>';
比如我想获取从现在到30天后每个星期五的日期。
目前,我可以利用下划线库和 moment.js 来做到这一点。但结果是超级冗长和烦人 procedural/imperative。观察:
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');
}
这里是对应的fiddle: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 用来制作惰性列表的函数。
I was wondering if there's a way to use Haskell-like features such as list comprehensions or infinite lists (lazy evaluation) to make this a lot more concise.
在 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 流失而言),但如果我理解正确的话,在 FP 中没有副作用比 memory/GC 效率更重要。
JS 有 Julian 日期库吗?这样事情就简单多了。
这里是一些伪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)
请注意,我在回答中使用了 stream.js and moment.js。因此,您必须在 HTML 中执行以下操作(请注意,我的语法是用 jade 编写的):
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()
是不是很漂亮?
快速解释:
- 我制作了一个无限的时刻(日期)对象流,代表从现在到永远的所有星期五
- 我获取(即检索)此流的元素直到第一时刻,这并不代表我的结束日期之前的时间。
就是这样!
请注意,takeWhile()
功能尚未在官方 stream.js 版本中实现,但我只是从其 lib 文件夹中复制并粘贴它。请参阅其 git 回购 here。
具有 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>';