我的问题如下:我试图编写一个函数,在给定环境中的对象上设置属性集合。我尝试模仿元数据层,就像SAS一样,因此您可以在变量上设置各种属性,如标签,小数位,日期格式等等。
示例:
SetAttributes(object = "list$dataframe$column", label="A label", width=20, decDigits=2,
dateTimeFormat="....", env=environment())
但是我必须在不同级别的对象上设置属性,比如说:
comment(list$dataframe$column) <- "comment on a column of a dataframe in a list"
comment(dataframe$column) <- "comment on a column of a dataframe"
comment(list) <- "comment on a list/dataframe/vector"
或者可以这样做:
comment("env[[list]][[dataframe]][[column]]) <- "text"
# (my function recognizes both formats, as a variable and as a string with chain of
# [[]] components).
所以我用这种方式实现了它:
SetAttributes <- function(varDescription, label="", .........., env=.GlobalEnv) {
parts <- strsplit( varDescription, "$", fixed=TRUE)[[1]]
if(length(parts) == 3) {
lst <- parts[1]
df <- parts[2]
col <- parts[3]
if(!is.na(label)) comment(env[[lst]][[df]][[col]]) <- label
if(!is.na(textWidth)) attr(env[[lst]][[df]][[col]], "width") <- textWidth
....
} else if(length(parts) == 2) {
df <- varTxtComponents[1]
col <- varTxtComponents[2]
if(!is.na(label)) comment(env[[df]][[col]]) <- label
if(!is.na(textWidth)) attr(env[[df]][[col]], "width") <- textWidth
....
} else if(length(parts) == 1) {
....
你现在看到了问题:我有三个类似代码的长度(部分)== 3,2和1
当我尝试以这种方式自动化时:
path <- c()
sapply(parts, FUN=function(comp){ path <<- paste0(path, "[[", comp, "]]") )}
comment(eval(parse(text=paste0(".GlobalEnv", path)))) <- "a comment"
我收到了错误:
Error in comment(eval(parse(text = paste0(".GlobalEnv", path)))) <- "a comment" :
target of assignment expands to non-language object
有没有办法在任何级别获取对象并为其设置属性而没有大量重复代码?
PS:是的,我听过千次从函数内部改变外部变量是邪恶的,所以请不要提及它。我知道自己想要达到的目标。答案 0 :(得分:3)
为了确保你听到它1001次,对于一个具有这种副作用的功能来说,这是一个非常糟糕的主意。这是一种非常类似于R的编程方式。如果您要编写R代码,那么以R方式执行操作会更好。这意味着返回可以选择重新分配的修改对象。这会让生活更轻松。
这是一个仅关注评论的简化版本。
SetComment <- function(varDescription, label=NULL, env=.GlobalEnv) {
obj <- parse(text= varDescription)[[1]]
eval(substitute(comment(X)<-Y, list(X=obj, Y=label)), env)
}
a<-list(b=4)
comment(a$b)
# NULL
SetComment("a$b", "check")
comment(a$b)
# [1] "check"
这里,我们不是解析和拆分字符串,而是构建一个我们在适当的上下文中评估的表达式。我们使用substitute()
弹出您想要实际调用的值。