请解释为什么会发生/不会发生以下错误:
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)
这是一个令人惊讶的微妙而复杂的问题。
尽管两段代码实际上都调用相同的函数,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)