【发布时间】:2015-03-18 21:54:40
【问题描述】:
简介
我有一个函数将日期作为输入,在一定时间内进行一些计算 - 由 Sys.sleep() 表示 - 删除日期中的所有 '-' 并返回一个字符:
library(maggritr)
auxialiaryCompute = function(vDate)
{
Sys.sleep(1)
vDate %>% as.character %>% gsub("-", "", .)
}
> auxialiaryCompute(as.Date("2015-01-14"))
[1] "20150114"
酷。上面的输出是'20150114'。现在我想在这个函数中包含以前的输出。或者前两天,或者.. n 之前的输出,直到过去的有限天称为loopBackMaxDate。
粗递归
这是一种可能的递归代码:
compute = function(vDate, loopBackMaxDate=vDate, loopBackDays=0)
{
d = as.Date # short alias
dates = Filter(function(x) x>d(loopBackMaxDate),
getPreviousDates(loopBackDays, d(vDate)))
if(length(dates)==0)
return(auxialiaryCompute(vDate=vDate, previousOutputs=list()))
previousOutputs = lapply(dates, function(u) compute(u, loopBackMaxDate, loopBackDays))
auxialiaryCompute(vDate=vDate, previousOutputs=previousOutputs)
}
auxialiaryCompute = function(vDate, previousOutputs=list())
{
Sys.sleep(1)
vDate %>% as.character %>% gsub("-", "", .)
}
getPreviousDates = function(loopBackDays, vDate)
{
if(loopBackDays==0) return()
seq.Date(from=vDate-loopBackDays, to=vDate-1, by="days")
}
有了这个,我得到了和以前一样的结果(平均花费 1 秒):
> compute(as.Date("2015-01-14"))
[1] "20150114"
以下内容需要4 秒:
> system.time(compute("2014-05-05", loopBackMaxDate="2014-05-01", loopBackDays=1))
user system elapsed
0.00 0.00 3.99
我要计算以下,需要 3 秒:
> system.time(compute("2014-05-04", loopBackMaxDate="2014-05-01", loopBackDays=1))
user system elapsed
0.02 0.00 3.01
这很糟糕,因为我正在再次计算 vDate="2014-05-04"、vDate="2014-05-03" 和 vDate="2014-05-02" 的结果,而在调用 compute("2014-05-05", loopBackMaxDate="2014-05-01", loopBackDays=1) 时已经完成...
记忆递归
这是我使用 memoized 的方式:
library(memoise)
compute = memoise(function(vDate, loopBackMaxDate=vDate, loopBackDays=0)
{
d = as.Date # short alias
dates = Filter(function(x) x>d(loopBackMaxDate), getPreviousDates(loopBackDays, d(vDate)))
if(length(dates)==0)
return(auxialiaryCompute(vDate=vDate, previousOutputs=list()))
previousOutputs = lapply(dates, function(u) compute(u, loopBackMaxDate, loopBackDays))
auxialiaryCompute(vDate=vDate, previousOutputs=previousOutputs)
})
auxialiaryCompute = memoise(function(vDate, previousOutputs=list())
{
Sys.sleep(1)
vDate %>% as.character %>% gsub("-", "", .)
})
首次运行(实际耗时 4 秒):
> system.time(compute("2014-05-05", loopBackMaxDate="2014-05-01", loopBackDays=1))
user system elapsed
0.00 0.00 4.01
第二次运行需要 1 秒,而我预计需要 0 秒:
> system.time(compute("2014-05-04", loopBackMaxDate="2014-05-01", loopBackDays=1))
user system elapsed
0.00 0.00 0.99
我认为我在某个地方完全错了......我可以将输出存储在一个全局变量中,但我真的想让它与记忆化或连续样式传递一起工作并避免冗余计算!
如果有人有想法,我将非常感激!
【问题讨论】:
-
TL;DR,但尽量避免在 R 中递归。另外,当你可以简单地使用
format时,为什么还要使用gsub和as.character? -
@Roland:确实,即使不影响问题的真实内容,它也更简单。
-
@bsg : Sys.sleep 在那里,以便测量函数以预期的迭代次数进行迭代(您也可以为这个玩具示例添加一些 loginfo 和 sprintf)。这也是一种有效检查记忆效果的方法。
-
非常感谢您的评论@Roland,您在日期问题上含蓄地指导了我!
标签: r recursion memoization memoise