Big*_*ist 13 r data.table
我遇到了这篇文章:来自Matt Dowle的http://r.789695.n4.nabble.com/speeding-up-perception-tp3640920p3646694.html,早些时候讨论过一些问题?data.table包的实施思路.
他使用以下代码:
x = list(a = 1:10000, b = 1:10000)
class(x) = "newclass"
"[<-.newclass" = function(x,i,j,value) x # i.e. do nothing
tracemem(x)
x[1, 2] = 42L
Run Code Online (Sandbox Code Playgroud)
具体来说,我在看:
"[<-.newclass" = function(x,i,j,value) x
Run Code Online (Sandbox Code Playgroud)
我试图了解那里做了什么以及如何使用这种表示法.
它看起来像我:
因此,我最好的猜测是我为现场修改定义了一个自定义函数(对于给定的类).
[<-.newclass 是类newclass的类修改.
了解发生的情况:通常以下代码应返回错误:
x = list(a = 1:10000, b = 1:10000)
x[1, 2] = 42L
Run Code Online (Sandbox Code Playgroud)
所以我想示例代码没有任何实际用途.
尝试使用逻辑:
一个简单的无意义尝试就是将要插入的值平方:
x[i, j] <- value^2
Run Code Online (Sandbox Code Playgroud)
全面尝试:
> x = matrix(1:9, 3, 3)
> class(x) = "newclass"
> "[<-.newclass" = function(x, i, j, value) x[i, j] <- value^2 # i.e. do something
> x[1, 2] = 9
Error: C stack usage 19923536 is too close to the limit
Run Code Online (Sandbox Code Playgroud)
这似乎不起作用.
我的问题:
"[<-.newclass" = function(x,i,j,value) x
Run Code Online (Sandbox Code Playgroud)
这种符号究竟是如何工作的,我将如何使用它?
(我添加data.table标签,因为链接的讨论是关于data.table中的"by-reference"到位修改,我认为).
Mik*_*ila 18
该`[<-`()功能(传统上)用于子分配,更广泛地说,是一种替换功能.它也是通用的(更具体地说,是一个内部泛型),它允许您为它编写自定义方法,正如您正确推测的那样.
通常,当您调用替换函数时,例如...
foo(x) <- bar(y)
Run Code Online (Sandbox Code Playgroud)
... <-(右边)的表达式(bar(y)作为第一个value参数)作为命名参数传递给`foo<-`()with x,并且x用结果重新分配对象:也就是说,所述调用等同于写入:
x <- `foo<-`(x, value = bar(y))
Run Code Online (Sandbox Code Playgroud)
因此,为了工作,所有替换函数必须至少有两个参数,其中一个必须命名value.大多数替换函数只有这两个参数,但也有例外:例如`attr<-`,通常是子分配.
当你有一个subassignment呼叫像x[i, j] <- y,i并j作为额外的参数给获得通过`[<-`()使用功能x和y作为第一个和value参数,分别为:
x <- `[<-`(x, i, j, value = y) # x[i, j] <- y
Run Code Online (Sandbox Code Playgroud)
在的情况下matrix或data.frame,i并且j将用于选择的行和列; 但总的来说,情况并非必须如此.自定义类的方法可以对参数执行任何操作.考虑这个例子:
x <- matrix(1:9, 3, 3)
class(x) <- "newclass"
`[<-.newclass` <- function(x, y, z, value) {
x + (y - z) * value # absolute nonsense
}
x[1, 2] <- 9
x
#> [,1] [,2] [,3]
#> [1,] -8 -5 -2
#> [2,] -7 -4 -1
#> [3,] -6 -3 0
#> attr(,"class")
#> [1] "newclass"
Run Code Online (Sandbox Code Playgroud)
这有用还是合理?可能不是.但它是有效的R代码吗?绝对!
在实际应用程序中查看自定义子分配方法的情况并不常见,因为`[<-`()通常"正常工作",正如您所期望的那样,基于类的基础对象.一个值得注意的例外是`[<-.data.frame`,底层对象是一个列表,但子分配的行为类似于矩阵.(另一方面,许多类确实需要自定义子设置方法,因为默认`[`()方法会删除大多数属性,包括class属性,请参阅?`[`详细信息).
至于为什么你的例子不起作用:记住你正在编写一个泛型函数的方法,并且所有常规规则都适用.如果我们`[<-`()在您的示例中使用函数形式并展开方法调度,我们可以立即看到它失败的原因:
`[<-.newclass` <- function(x, i, j, value) {
x <- `[<-.newclass`(x, i, j, value = value^2) # x[i, j] <- value^2
}
Run Code Online (Sandbox Code Playgroud)
也就是说,函数是递归定义的,没有基本情况,导致无限循环.解决这个问题的一种方法是unclass(x)在调用下一个方法之前:
`[<-.newclass` <- function(x, i, j, value) {
x <- unclass(x)
x[i, j] <- value^2
x # typically you would also add the class back here
}
Run Code Online (Sandbox Code Playgroud)
(或者,使用一种更高级的技术,也可以使用如下明确的下一个方法替换正文:NextMethod(value = value^2).这对继承和超类来说更好.)
只是为了验证它是否有效:
x <- matrix(1:9, 3, 3)
class(x) <- "newclass"
x[1, 2] <- 9
x
#> [,1] [,2] [,3]
#> [1,] 1 81 7
#> [2,] 2 5 8
#> [3,] 3 6 9
Run Code Online (Sandbox Code Playgroud)
完全令人困惑!
至于Dowle的"无所事事"子分配示例的背景,我相信这是为了说明在R 2.13.0中,自定义子分配方法总是会导致对象的深层副本,即使方法本身也是如此什么都没有.(这不再是这种情况,因为我相信R 3.1.0.)
由reprex包(v0.2.0)创建于2018-08-15 .