动态命名要更新的对象
Dynamically naming objects to be updated
我已经定义了一个 S4 Class,它的插槽是一个列表。我已经编写了一个方法(基于 Genolini 对 S4 的介绍 - 第 10.2 节)来向该列表追加一个新条目:
setClass("MyClass",
slots = c(entries = "list")
)
a1 <- new("MyClass", entries = list(1))
setGeneric(name="MyAppend",
def=function(.Object, newEntry)
{
standardGeneric("MyAppend")
}
)
setMethod(f = "MyAppend",
signature = "MyClass",
definition = function(.Object, newEntry){
nameObject <- deparse(substitute(.Object))
newlist <- .Object@entries
n <- newlist %>% length
newlist[[n + 1]] <- newEntry
.Object@entries <- newlist
assign(nameObject, .Object, envir = parent.frame())
return(invisible)
}
)
如果我那么运行
MyAppend(a1, 2)
a1
我明白了
R>a1
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
这是应该的。
但在我的应用程序中,我将生成要动态更新的对象的名称:
ObjectName <- paste0("a", 1)
然后我可以使用
将该名称变成对象本身
Object <- ObjectName %>% sym %>% eval
然后是str(Object)
returns
Formal class 'MyClass' [package ".GlobalEnv"] with 1 slot
..@ entries:List of 3
.. ..$ : num 1
.. ..$ : num 2
这又是理所应当的。
但是当我运行
MyAppend(Object, 3)
Object
a1
我得到以下信息,表明 Object
已更新 a1
尚未更新。
R>Object
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
R>
R>a1
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
请问我做错了什么?
问题是这一行:
Object <- ObjectName %>% sym %>% eval
并不像您认为的那样。右边的计算结果为对象 a1
,因此与执行
没有什么不同
Object <- a1
但这创建了 a1
的 copy,它不会创建 a1
的引用或指针或同义词。
可以通过传递您希望附加到您的对象的未评估名称来创建(某种)引用通用方法。如果您省略了 ObjectName %>% sym %>% eval
的 eval
部分,那么对象将被分配 name a1
,它可以作为对 [=] 的引用传递45=]对象a1
.
然而,这给您带来了一个新问题:MyAppend
不知道如何处理 class name
的对象。因此,您需要编写一个合适的方法来处理名称:
setMethod(f = "MyAppend",
signature = "name",
definition = function(.Object, newEntry){
stopifnot(class(eval(.Object)) == "MyClass")
objname <- as.character(.Object)
.Object <- eval(.Object)
.Object@entries <- append(.Object@entries, newEntry)
assign(as.character(objname), .Object, envir = parent.frame())
}
)
现在让我们看看它是如何工作的:
a1 <- new("MyClass", entries = list(1))
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
MyAppend(a1, 2)
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
Object <- paste0("a", 1) %>% sym()
MyAppend(Object, 3)
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
我认为这就是您的意图。您可能希望考虑使用一种方法来分派字符串以简化此工作流程(您可以在方法内部使用 get
从作为字符串传递的名称中检索对象)
请注意,我也更改了您自己的功能;你不应该做 return(invisible)
,因为这个 return 是内置函数 invisible
的主体。只需完全删除 return 语句即可。您还可以使用内置函数 append
,使 MyClass
的方法更简单一些:
setMethod(f = "MyAppend",
signature = "MyClass",
definition = function(.Object, newEntry){
nameObject <- deparse(substitute(.Object))
.Object@entries <- append(.Object@entries, newEntry)
assign(nameObject, .Object, envir = parent.frame())
}
)
正如接受的答案所提到的,行
Object <- ObjectName %>% sym %>% eval
未按预期运行。
如果你想使用动态生成的名称,你需要使用的两个函数是 get
(获取与名称相关联的对象)和 assign
(分配给名称为你计算)。两者都有全面的帮助页面。
几乎没有其他方法可以工作(eval(parse(text=paste0(...)))
除外,但出于多种原因不推荐编程实践。
我已经定义了一个 S4 Class,它的插槽是一个列表。我已经编写了一个方法(基于 Genolini 对 S4 的介绍 - 第 10.2 节)来向该列表追加一个新条目:
setClass("MyClass",
slots = c(entries = "list")
)
a1 <- new("MyClass", entries = list(1))
setGeneric(name="MyAppend",
def=function(.Object, newEntry)
{
standardGeneric("MyAppend")
}
)
setMethod(f = "MyAppend",
signature = "MyClass",
definition = function(.Object, newEntry){
nameObject <- deparse(substitute(.Object))
newlist <- .Object@entries
n <- newlist %>% length
newlist[[n + 1]] <- newEntry
.Object@entries <- newlist
assign(nameObject, .Object, envir = parent.frame())
return(invisible)
}
)
如果我那么运行
MyAppend(a1, 2)
a1
我明白了
R>a1
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
这是应该的。
但在我的应用程序中,我将生成要动态更新的对象的名称:
ObjectName <- paste0("a", 1)
然后我可以使用
将该名称变成对象本身Object <- ObjectName %>% sym %>% eval
然后是str(Object)
returns
Formal class 'MyClass' [package ".GlobalEnv"] with 1 slot
..@ entries:List of 3
.. ..$ : num 1
.. ..$ : num 2
这又是理所应当的。
但是当我运行
MyAppend(Object, 3)
Object
a1
我得到以下信息,表明 Object
已更新 a1
尚未更新。
R>Object
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
R>
R>a1
An object of class "MyClass"
Slot "entries":
[[1]]
[1] 1
[[2]]
[1] 2
请问我做错了什么?
问题是这一行:
Object <- ObjectName %>% sym %>% eval
并不像您认为的那样。右边的计算结果为对象 a1
,因此与执行
Object <- a1
但这创建了 a1
的 copy,它不会创建 a1
的引用或指针或同义词。
可以通过传递您希望附加到您的对象的未评估名称来创建(某种)引用通用方法。如果您省略了 ObjectName %>% sym %>% eval
的 eval
部分,那么对象将被分配 name a1
,它可以作为对 [=] 的引用传递45=]对象a1
.
然而,这给您带来了一个新问题:MyAppend
不知道如何处理 class name
的对象。因此,您需要编写一个合适的方法来处理名称:
setMethod(f = "MyAppend",
signature = "name",
definition = function(.Object, newEntry){
stopifnot(class(eval(.Object)) == "MyClass")
objname <- as.character(.Object)
.Object <- eval(.Object)
.Object@entries <- append(.Object@entries, newEntry)
assign(as.character(objname), .Object, envir = parent.frame())
}
)
现在让我们看看它是如何工作的:
a1 <- new("MyClass", entries = list(1))
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
MyAppend(a1, 2)
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
Object <- paste0("a", 1) %>% sym()
MyAppend(Object, 3)
a1
#> An object of class "MyClass"
#> Slot "entries":
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
我认为这就是您的意图。您可能希望考虑使用一种方法来分派字符串以简化此工作流程(您可以在方法内部使用 get
从作为字符串传递的名称中检索对象)
请注意,我也更改了您自己的功能;你不应该做 return(invisible)
,因为这个 return 是内置函数 invisible
的主体。只需完全删除 return 语句即可。您还可以使用内置函数 append
,使 MyClass
的方法更简单一些:
setMethod(f = "MyAppend",
signature = "MyClass",
definition = function(.Object, newEntry){
nameObject <- deparse(substitute(.Object))
.Object@entries <- append(.Object@entries, newEntry)
assign(nameObject, .Object, envir = parent.frame())
}
)
正如接受的答案所提到的,行
Object <- ObjectName %>% sym %>% eval
未按预期运行。
如果你想使用动态生成的名称,你需要使用的两个函数是 get
(获取与名称相关联的对象)和 assign
(分配给名称为你计算)。两者都有全面的帮助页面。
几乎没有其他方法可以工作(eval(parse(text=paste0(...)))
除外,但出于多种原因不推荐编程实践。