英文:
How to store a list of variables generated within sourced R code
问题
我正在尝试使用不同的源文件填充R6类的公共和私有字段。为此,我创建了一个小型编译器脚本,用于源文件并提取公共和私有方法(参见下面的示例)。然后,编译器读取代码并将结果存储在以前定义的环境中,然后用于填充类的字段。
这基本上可以工作。问题是,如下面的示例所示,中间变量(variable1.1、variable2等)也会保存,除非在源脚本内部进行清理。
# 源代码示例(testSource.R)
public <- list()
variable1 <- "A"
public["variable1"] <- variable1
variable1.1 <- "B"
variable2 <- paste0(variable1, variable1.1)
public["variable2"] <- variable2
private <- list()
variable3 <- "C"
private["variable3"] <- variable3
variable3.1 <- "D"
variable4 <- paste0(variable3, variable3.1)
private["variable4"] <- variable4
# 编译器脚本
myPath <- "~/random/path/"
myEnv <- new.env()
file.sources <- list.files(myPath, full.names = TRUE)
sapply(file.sources, \(x){source(x, local = myEnv)})
public_list <- grep("public", names(myEnv), value = TRUE)
private_list <- grep("private", names(myEnv), value = TRUE)
# 连接公共方法
public <- list()
for (pub in public_list){
public <- c(public, myEnv[[pub]])
}
rm(pub, public_list)
# 连接私有方法
private <- list()
for (pr in private_list){
private <- c(private, myEnv[[pr]])
}
rm(pr, private_list)
# 创建类
myClass <- R6Class("myClass", lock_class = FALSE, lock_objects = FALSE,
public = public,
private = private)
在上面的示例中,我可以使用类似以下的方法来选择环境中仅包含列表的内容:
envList <- as.list.environment(myEnv)
envList <- envList[sapply(names(envList), \(x){typeof(envList[[x]]) == "list"})]
# ...
但这依赖于遵循特定的做法(例如,在列表上保存相关的输出),这可能很难在多人协作中强制执行。
是否有更好的方法来保存源代码中的变量,而无需依赖环境作为中间步骤?
谢谢!
英文:
I am trying to populate the public and private fields of an R6 class using separate sourced files. For that
I have created a small compiler script that sources the files and greps public and private methods (see example below). The compiler then reads in the code and stores the results (which could in principle be stored in multiple files) into a previously defined environment, which is then used to populate the fields of the class.
This works relatively ok. The problem is that, as seen in the example below, the intermediary variables (variable1.1, variable2, etc) are also saved unless cleaned within the sourced script.
#example of sourced code (testSource.R)
public<- list()
variable1<-"A"
public["variable1"]<-variable1
variable1.1<- "B"
variable2<- paste0(variable1,variable1.1)
public["variable2"]<-variable2
private<-list()
variable3<-"C"
private["variable3"]<-variable3
variable3.1<- "D"
variable4<- paste0(variable3,variable3.1)
private["variable4"]<-variable4
# compiler script
myPath<-"~/random/path/"
myEnv <- new.env()
file.sources<-list.files(myPath,full.names = TRUE)
sapply(file.sources,\(x){source(x,local=myEnv)})
public_list <- grep("public",names(myEnv),value=TRUE)
private_list <-grep("private",names(myEnv),value=TRUE)
# concatenate public methods
public<- list()
for (pub in public_list){
public<-c(public,myEnv[[pub]])
}
rm(pub,public_list)
#concatenate private methods
private<- list()
for (pr in private_list){
private<-c(private,myEnv[[pr]])
}
rm(pr,private_list)
# Create the Class
myClass<- R6Class("myClass",lock_class = FALSE, lock_objects=FALSE,
public= public,
private = private)
In the example above I could subset the environment to select only the lists with something like this:
envList<-as.list.environment(myEnv)
envList<-envList[sapply(names(envList), \(x){typeof(envList[[x]])=="list"})]
#...
But this relies on adhering to specific practices (e.g. saving relevant outputs on lists), which may be difficult to enforce with many people working in collaboration.
Is there a better way of saving the variables within the sourced code, without relying on environments as an intermediary step?
(used input from link1 and link2 )
Thanks!
答案1
得分: 1
以下是您要翻译的内容:
"What is quite interesting is that you noticed you needed to limit the scope of the variables into an environment so you can source/delete the variables afterwards."
"在这里非常有趣的是您注意到您需要将变量的范围限制在一个环境中,这样您以后就可以对变量进行源代码引入/删除。"
"In my opinion a much easier way to do this is to use functions since they will encapsulate their own environment and will only export to the outer world what you return."
"在我看来,一个更容易的方法是使用函数,因为它们将封装自己的环境,并且只会将您返回的内容导出到外部世界。"
"But this relies on adhering to specific practices (e.g. saving relevant outputs on lists), which may be difficult to enforce with many people working in collaboration."
"但这依赖于遵循特定的实践(例如,在列表上保存相关的输出),这在与许多人合作的情况下可能很难强制执行。"
"Best to do this is to design a package that everyone in the team relies on and apply the best practices in the package. The user will be happy the code is working and the developer will be happy the practices will be enforced. If you don't want to build a whole package, just develop functions and ask the team to use the functions instead of writing their own scripts."
"最好的方法是设计一个团队中的每个人都依赖的包,并在包中应用最佳实践。用户会高兴代码正常工作,开发人员会高兴实践得到强制执行。如果您不想构建一个完整的包,只需开发函数,并要求团队使用这些函数而不是编写自己的脚本。"
"Here is my take on this:"
"这是我对此的看法:"
# --- sources1.R
public<- list()
variable1<-"A"
public["variable1"]<-variable1
variable1.1<- "B"
variable2<- paste0(variable1,variable1.1)
public["variable2"]<-variable2
private<-list()
variable3<-"C"
private["variable3"]<-variable3
variable3.1<- "D"
variable4<- paste0(variable3,variable3.1)
private["variable4"]<-variable4
# --- sources2.R
public<- list()
variable1<-"1"
public["variable1"]<-variable1
variable1.1<- "2"
variable2<- paste0(variable1,variable1.1)
public["variable2"]<-variable2
private<-list()
variable3<-"3"
private["variable3"]<-variable3
private["variable5"] <- "ABCDE"
variable3.1<- "4"
variable4<- paste0(variable3,variable3.1)
private["variable4"]<-variable4
# -- functions
library(R6)
read_source <- function(file) {
source(file, local = TRUE) # (local = TRUE) is important or the variables will leak
list(private = private, public = public)
}
merge_source <- function(lhs, rhs) {
# -----------------------------------------
# merge rhs into lhs
# -- ideally need to manage collisions but
# -- this will just overwrite the variables
# -- for now
# -----------------------------------------
for(scope in c("private", "public")) {
for(vars in names(rhs[[scope]]) {
lhs[[scope]][[vars]] <- rhs[[scope]][[vars]]
}
}
lhs
}
read_and_combine <- function(sources) {
# - simple map + reduce
scope <- lapply(sources, read_source)
scope <- Reduce(merge_source, scope)
scope
}
build_class <- function(class_name, sources) {
scope <- read_and_combine(sources)
R6Class(
class_name,
lock_class = FALSE,
lock_objects = FALSE,
public = scope$public,
private = scope$private
)
}
sources <- c("source1.R", "source2.R")
my_class <- build_class("myClass", sources)
my_class
# - you can check no variable from any script leaks into the Global Environment
ls(envir = .GlobalEnv)
I have provided translations for the code comments as well.
英文:
What is quite interesting is that you noticed you needed to limit the scope of the variables into an environment so you can source/delete the variables afterwards.
In my opinion a much easier way to do this is to use functions since they will encapsulate their own environment and will only export to the outer world what you return.
> But this relies on adhering to specific practices (e.g. saving relevant outputs on lists), which may be difficult to enforce with many people working in collaboration.
Best to do this is to design a package that everyone in the team relies on and apply the best practices in the package. The user will be happy the code is working and the developer will be happy the practices will be enforced. If you don´t want to build a whole package, just develop functions and ask the team to use the functions instead of writing their own scripts.
Here is my take on this:
# --- sources1.R
public<- list()
variable1<-"A"
public["variable1"]<-variable1
variable1.1<- "B"
variable2<- paste0(variable1,variable1.1)
public["variable2"]<-variable2
private<-list()
variable3<-"C"
private["variable3"]<-variable3
variable3.1<- "D"
variable4<- paste0(variable3,variable3.1)
private["variable4"]<-variable4
# --- sources2.R
public<- list()
variable1<-"1"
public["variable1"]<-variable1
variable1.1<- "2"
variable2<- paste0(variable1,variable1.1)
public["variable2"]<-variable2
private<-list()
variable3<-"3"
private["variable3"]<-variable3
private["variable5"] <- "ABCDE"
variable3.1<- "4"
variable4<- paste0(variable3,variable3.1)
private["variable4"]<-variable4
# -- functions
library(R6)
read_source <- function(file) {
source(file, local = TRUE) # (local = TRUE) is important or the variables will leak
list(private = private, public = public)
}
merge_source <- function(lhs, rhs) {
# -----------------------------------------
# merge rhs into lhs
# -- ideally need to manage collisions but
# -- this will just overwrite the variables
# -- for now
# -----------------------------------------
for(scope in c("private", "public")) {
for(vars in names(rhs[[scope]])) {
lhs[[scope]][[vars]] <- rhs[[scope]][[vars]]
}
}
lhs
}
read_and_combine <- function(sources) {
# - simple map + reduce
scope <- lapply(sources, read_source)
scope <- Reduce(merge_source, scope)
scope
}
build_class <- function(class_name, sources) {
scope <- read_and_combine(sources)
R6Class(
class_name,
lock_class = FALSE,
lock_objects = FALSE,
public = scope$public,
private = scope$private
)
}
sources <- c("source1.R", "source2.R")
my_class <- build_class("myClass", sources)
my_class
# - you can check no variable from any script leaks into the Global Environment
ls(envir = .GlobalEnv)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论