R 中列表元素的惰性求值
Lazy Evaluation of a List Element in R
有没有办法延迟加载列表的元素?
我有一个很大的 data.frames 列表,每个列表都需要很长时间才能生成和加载。通常我不会在会话期间使用所有 data.frames,所以希望它们在我使用它们时延迟生成和加载。我知道我可以使用 delayedAssign
创建延迟加载的变量,但这不能应用于列表元素。
以下是无效的可重现示例:
一些需要一段时间才能生成的函数data.frames:
slow_fun_1 <- function(){
cat('running slow function 1 now \n')
Sys.sleep(1)
df<-data.frame(x=1:5, y=6:10)
return(df)
}
slow_fun_2 <- function(){
cat('running slow function 2 now \n')
Sys.sleep(1)
df<-data.frame(x=11:15, y=16:20)
return(df)
}
方法 1
my_list <- list()
my_list$df_1 <-slow_fun_1()
my_list$df_2 <-slow_fun_2()
# This is too slow. I might not want to use both df_1 & df_2.
方法 2
my_list_2 <- list()
delayedAssign('my_list_2$df_1', slow_fun_1())
delayedAssign('my_list_2$df_2', slow_fun_2())
# Does not work. Can't assign to a list.
my_list_2 #output is still an empty list.
这是一个不完美的解决方案。它是不完美的,因为如果不加载所有列表元素,则无法在 Rstudio 控制台中以交互方式使用该列表。具体来说,当输入 $
时,Rstudio 会运行这两个函数。 my_env$df_1
上的 Ctrl+Enter
按预期工作,因此问题在于控制台中的使用。
my_env <- new.env()
delayedAssign('df_1',slow_fun_1(),assign.env = my_env)
delayedAssign('df_2',slow_fun_2(),assign.env = my_env)
# That was very fast!
get('df_1',envir = my_env)
my_env$df_1
# only slow_fun_1() is run once my_env$df_1 is called. So this is a partial success.
# however it does not work interactively in Rstudio
# when the following is typed into the console:
my_env$
# In Rstudio, once the dollar sign is typed, both functions are run.
# this works interactively in the Rstudio console.
# But the syntax is less convenient to type.
my_env[['d']]
这是一种可能的解决方案。这不是懒惰的评估。但是它会在您需要时计算 data.frame (然后将其缓存,因此仅在第一次进行计算)。您可以使用包 memoise
来实现这一点。例如
slow_fun_1 <- function(){
cat('running slow function 1 now \n')
Sys.sleep(1)
df<-data.frame(x=1:5, y=6:10)
return(df)
}
slow_fun_2 <- function(){
cat('running slow function 2 now \n')
Sys.sleep(1)
df<-data.frame(x=11:15, y=16:20)
return(df)
}
library(memoise)
my_list <- list()
my_list$df_1 <-memoise(slow_fun_1)
my_list$df_2 <-memoise(slow_fun_2)
并注意 my_list$df_1
等等实际上是给你 data.frame 的函数,所以你的用法应该是这样的:
> my_list$df_1()
running slow function 1 now
x y
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
> my_list$df_1()
x y
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
>
请注意,缓存函数仅在第一次进行实际计算。
更新:如果你想在没有函数调用的情况下坚持原来的用法,一种方法是根据列表修改数据结构,例如:
library(memoise)
lazy_list <- function(...){
structure(list(...), class = c("lazy_list", "list"))
}
as.list.lazy_list <- function(x){
structure(x, class = "list")
}
generator <- function(f){
structure(memoise(f), class = c("generator", "function"))
}
`$.lazy_list` <- function(lst, name){
r <- as.list(lst)[[name]]
if (inherits(r, "generator")) {
return(r())
}
return(r)
}
`[[.lazy_list` <- function(lst, name){
r <- as.list(lst)[[name]]
if (inherits(r, "generator")) {
return(r())
}
return(r)
}
lazy1 <- lazy_list(df_1 = generator(slow_fun_1),
df_2 = generator(slow_fun_2),
df_3 = data.frame(x=11:15, y=16:20))
lazy1$df_1
lazy1$df_1
lazy1$df_2
lazy1$df_2
lazy1$df_3
有没有办法延迟加载列表的元素?
我有一个很大的 data.frames 列表,每个列表都需要很长时间才能生成和加载。通常我不会在会话期间使用所有 data.frames,所以希望它们在我使用它们时延迟生成和加载。我知道我可以使用 delayedAssign
创建延迟加载的变量,但这不能应用于列表元素。
以下是无效的可重现示例:
一些需要一段时间才能生成的函数data.frames:
slow_fun_1 <- function(){
cat('running slow function 1 now \n')
Sys.sleep(1)
df<-data.frame(x=1:5, y=6:10)
return(df)
}
slow_fun_2 <- function(){
cat('running slow function 2 now \n')
Sys.sleep(1)
df<-data.frame(x=11:15, y=16:20)
return(df)
}
方法 1
my_list <- list()
my_list$df_1 <-slow_fun_1()
my_list$df_2 <-slow_fun_2()
# This is too slow. I might not want to use both df_1 & df_2.
方法 2
my_list_2 <- list()
delayedAssign('my_list_2$df_1', slow_fun_1())
delayedAssign('my_list_2$df_2', slow_fun_2())
# Does not work. Can't assign to a list.
my_list_2 #output is still an empty list.
这是一个不完美的解决方案。它是不完美的,因为如果不加载所有列表元素,则无法在 Rstudio 控制台中以交互方式使用该列表。具体来说,当输入 $
时,Rstudio 会运行这两个函数。 my_env$df_1
上的 Ctrl+Enter
按预期工作,因此问题在于控制台中的使用。
my_env <- new.env()
delayedAssign('df_1',slow_fun_1(),assign.env = my_env)
delayedAssign('df_2',slow_fun_2(),assign.env = my_env)
# That was very fast!
get('df_1',envir = my_env)
my_env$df_1
# only slow_fun_1() is run once my_env$df_1 is called. So this is a partial success.
# however it does not work interactively in Rstudio
# when the following is typed into the console:
my_env$
# In Rstudio, once the dollar sign is typed, both functions are run.
# this works interactively in the Rstudio console.
# But the syntax is less convenient to type.
my_env[['d']]
这是一种可能的解决方案。这不是懒惰的评估。但是它会在您需要时计算 data.frame (然后将其缓存,因此仅在第一次进行计算)。您可以使用包 memoise
来实现这一点。例如
slow_fun_1 <- function(){
cat('running slow function 1 now \n')
Sys.sleep(1)
df<-data.frame(x=1:5, y=6:10)
return(df)
}
slow_fun_2 <- function(){
cat('running slow function 2 now \n')
Sys.sleep(1)
df<-data.frame(x=11:15, y=16:20)
return(df)
}
library(memoise)
my_list <- list()
my_list$df_1 <-memoise(slow_fun_1)
my_list$df_2 <-memoise(slow_fun_2)
并注意 my_list$df_1
等等实际上是给你 data.frame 的函数,所以你的用法应该是这样的:
> my_list$df_1()
running slow function 1 now
x y
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
> my_list$df_1()
x y
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
>
请注意,缓存函数仅在第一次进行实际计算。
更新:如果你想在没有函数调用的情况下坚持原来的用法,一种方法是根据列表修改数据结构,例如:
library(memoise)
lazy_list <- function(...){
structure(list(...), class = c("lazy_list", "list"))
}
as.list.lazy_list <- function(x){
structure(x, class = "list")
}
generator <- function(f){
structure(memoise(f), class = c("generator", "function"))
}
`$.lazy_list` <- function(lst, name){
r <- as.list(lst)[[name]]
if (inherits(r, "generator")) {
return(r())
}
return(r)
}
`[[.lazy_list` <- function(lst, name){
r <- as.list(lst)[[name]]
if (inherits(r, "generator")) {
return(r())
}
return(r)
}
lazy1 <- lazy_list(df_1 = generator(slow_fun_1),
df_2 = generator(slow_fun_2),
df_3 = data.frame(x=11:15, y=16:20))
lazy1$df_1
lazy1$df_1
lazy1$df_2
lazy1$df_2
lazy1$df_3