当通过 base:: 限定时,`[.factor` 会引发 NextMethod 错误

use*_*536 6 oop r

请解释为什么会发生/不会发生以下错误:

ff = factor(1:3)
`[.factor`(ff) # okay
base::`[.factor`(ff)  # Error in NextMethod("[") : 'NextMethod' called from an anonymous function
f = `[.factor`; f(ff) # Error in NextMethod("[") : wrong value for .Method
Run Code Online (Sandbox Code Playgroud)

All*_*ron 6

这是一个令人惊讶的微妙而复杂的问题。

尽管两段代码实际上都调用相同的函数,base::`[.factor`(ff)但不起作用和起作用的原因是因为工作方式不寻常。TL;DR 是s符号,但是是一个调用`[.factor`(ff)`NextMethod`[.factor`base::`[.factor`

当您调用 时NextMethod底层 C 代码会计算出它是在什么上下文中调用的,并且如果不满足任何一个条件,就会抛出错误。

其中一个条件是所在的函数NextMethod必须已被直接调用 - 用技术术语来说,调用中的第一个节点必须是符号而不是表达式。如果您尝试通过调用获取该函数,NextMethod将会检测到这一点并给出“匿名函数”错误。

NextMethod还检查函数的名称是否与所使用的通用名称一致,如果不匹配,我们会得到“错误的值.Method”错误

我们可以用一个简单的例子来演示相同的行为。我们有一个“foo”类的对象:

foo <- structure(1:3, class = c("foo"))

foo
#> [1] 1 2 3
#> attr(,"class")
#> [1] "foo"
Run Code Online (Sandbox Code Playgroud)

我们希望能够对“foo”进行子集化,以便“foo”的任何子集仍然属于“foo”类,但除此之外,我们希望与数值向量具有完全相同的子集规则。这就是NextMethod出现的地方:

`[.foo` <- function(obj, ind) {
  y <- NextMethod("[")
  class(y) <- "foo"
  y
}


foo[2]
#> [1] 2
#> attr(,"class")
#> [1] "foo"
Run Code Online (Sandbox Code Playgroud)

这按预期工作,直接调用它也是如此:

`[.foo`(foo, 2)
#> [1] 2
Run Code Online (Sandbox Code Playgroud)

但是,如果我们尝试间接调用该函数(例如via get),则会NextMethod抛出错误,因为这get("[.foo")是一个调用,而不是一个符号。

get("[.foo")(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function
Run Code Online (Sandbox Code Playgroud)

即使将调用括在括号或大括号中也足以生成错误(因为这些括号实际上也是调用)

(`[.foo`)(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function

{`[.foo`}(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function
Run Code Online (Sandbox Code Playgroud)

如果我们尝试重命名该函数,我们会得到与尝试重命名相同的错误[.factor

f <- `[.foo`

f(foo, 2)
#> Error in NextMethod("[") : wrong value for .Method
Run Code Online (Sandbox Code Playgroud)

现在f至少是一个符号,但它是错误的符号(f不是[

最后一个难题是为什么base::`[.factor`不是一个符号。这是因为在 R 中,命名空间符号::实际上是一个call,所以base::`[.factor`实际上被解析为`::`(base, `[.factor`)

class(quote(base::`[.factor`))
#> [1] "call"
Run Code Online (Sandbox Code Playgroud)

然而,如果没有名称空间标识符,我们所拥有的是一个符号(又名“名称”)

class(quote(`[.factor`))
#> [1] "name"
Run Code Online (Sandbox Code Playgroud)