如何存储在源R代码中生成的变量列表

huangapple go评论79阅读模式
英文:

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"})]

# ...

但这依赖于遵循特定的做法(例如,在列表上保存相关的输出),这可能很难在多人协作中强制执行。

是否有更好的方法来保存源代码中的变量,而无需依赖环境作为中间步骤?

(使用了链接1链接2中的输入)

谢谢!

英文:

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&lt;- list() 

variable1&lt;-&quot;A&quot;

public[&quot;variable1&quot;]&lt;-variable1

variable1.1&lt;- &quot;B&quot;

variable2&lt;- paste0(variable1,variable1.1)

public[&quot;variable2&quot;]&lt;-variable2

private&lt;-list()

variable3&lt;-&quot;C&quot;

private[&quot;variable3&quot;]&lt;-variable3

variable3.1&lt;- &quot;D&quot;

variable4&lt;- paste0(variable3,variable3.1)

private[&quot;variable4&quot;]&lt;-variable4
# compiler script

myPath&lt;-&quot;~/random/path/&quot;

myEnv &lt;- new.env()    

file.sources&lt;-list.files(myPath,full.names = TRUE)

sapply(file.sources,\(x){source(x,local=myEnv)})


public_list &lt;- grep(&quot;public&quot;,names(myEnv),value=TRUE)

private_list &lt;-grep(&quot;private&quot;,names(myEnv),value=TRUE)

# concatenate public methods

public&lt;- list()

for (pub in public_list){
  public&lt;-c(public,myEnv[[pub]])
}

rm(pub,public_list)

#concatenate private methods

private&lt;- list()

for (pr in private_list){
  private&lt;-c(private,myEnv[[pr]])
}
rm(pr,private_list)

# Create the Class 

myClass&lt;- R6Class(&quot;myClass&quot;,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&lt;-as.list.environment(myEnv)

envList&lt;-envList[sapply(names(envList), \(x){typeof(envList[[x]])==&quot;list&quot;})]

#...

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&lt;- list() 

variable1&lt;-&quot;A&quot;

public[&quot;variable1&quot;]&lt;-variable1

variable1.1&lt;- &quot;B&quot;

variable2&lt;- paste0(variable1,variable1.1)

public[&quot;variable2&quot;]&lt;-variable2

private&lt;-list()

variable3&lt;-&quot;C&quot;

private[&quot;variable3&quot;]&lt;-variable3

variable3.1&lt;- &quot;D&quot;

variable4&lt;- paste0(variable3,variable3.1)

private[&quot;variable4&quot;]&lt;-variable4
# --- sources2.R

public&lt;- list() 

variable1&lt;-&quot;1&quot;

public[&quot;variable1&quot;]&lt;-variable1

variable1.1&lt;- &quot;2&quot;

variable2&lt;- paste0(variable1,variable1.1)

public[&quot;variable2&quot;]&lt;-variable2

private&lt;-list()

variable3&lt;-&quot;3&quot;

private[&quot;variable3&quot;]&lt;-variable3
private[&quot;variable5&quot;] &lt;- &quot;ABCDE&quot;

variable3.1&lt;- &quot;4&quot;

variable4&lt;- paste0(variable3,variable3.1)

private[&quot;variable4&quot;]&lt;-variable4
# -- functions

library(R6)

read_source &lt;- function(file) {
  source(file, local = TRUE)  # (local = TRUE) is important or the variables will leak
  list(private = private, public = public)
}

merge_source &lt;- 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(&quot;private&quot;, &quot;public&quot;)) {
    for(vars in names(rhs[[scope]])) {
      lhs[[scope]][[vars]] &lt;- rhs[[scope]][[vars]]
    }    
  }
  
  lhs 
}

read_and_combine &lt;- function(sources) {
  # - simple map + reduce
  scope &lt;- lapply(sources, read_source)
  scope &lt;- Reduce(merge_source, scope)
  scope
}


build_class &lt;- function(class_name, sources) {
  scope &lt;- read_and_combine(sources)
  R6Class(
    class_name,
    lock_class = FALSE,
    lock_objects = FALSE,
    public = scope$public,
    private = scope$private
  )
}

sources &lt;- c(&quot;source1.R&quot;, &quot;source2.R&quot;)
my_class &lt;- build_class(&quot;myClass&quot;, sources)
my_class

# - you can check no variable from any script leaks into the Global Environment

ls(envir = .GlobalEnv)

huangapple
  • 本文由 发表于 2023年2月27日 18:29:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75579295.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定