所以我现在正处于项目中间,而且我遇到了范围问题。以下是我的问题的简化。
a <- function() {
x <- 1:3 ## x is defined in a()
w <- d()
x
w
}
b <- function() {
y <- x[2] ## I want to access x from b()
x <- x[-2] ## and modify x
return(y)
}
d <- function() {
z <- b() ## then call b() from d()
return(list(1, z, 3, 4))
}
a()
当我运行a()
时,理想输出为w
等于list(1, 2, 3, 4)
且x
等于c(1, 3)
。我知道我可以在全球范围内定义x
,但x
应该是不可见的&#39;到d()
。
我尝试在parent.frame()
中使用b()
,但它不起作用,可能是因为d()
是其父级而不是a()
。我也和environment()
搞混了,但我在理解结构方面遇到了麻烦。
答案 0 :(得分:1)
1)本地环境我们可以在共享x
的本地环境中定义功能。由于x
,b
和d
仅在内部使用,因此我们只需将a
提供回工作区。
a <- local({
x <- NULL
b <- function() { y <- x[2]; x <<- x[-2]; y }
d <- function() { z <- b(); list(1, z, 3, 4) }
function() { x <<- 1:3; d() }
})
,并提供:
> x # x should not be visible
Error: object 'x' not found
> a()
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
2)嵌套函数另一种方法是嵌套函数:
a <- function() {
b <- function() { y <- x[2]; x <<- x[-2]; y }
d <- function() { z <- b(); list(1, z, 3, 4) }
x <- 1:3
d()
}
a()
,并提供:
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
3)使用对象模型
虽然这允许我们在a
不可见的情况下运行x
,但最好使用其中一个R对象模型来定义a
,{{1}的对象}和b
是该对象的方法,d
是属性。
proto包实现了一个基于纯对象的模型(即没有类),这在这里似乎特别适用:
x
,并提供:
library(proto)
p <- proto(x = NULL,
b = function(.) { y <- .$x[2]; .$x <- .$x[-2]; y},
d = function(.) { z <- .$b(); list(1, z, 3, 4) },
a = function(.) { .$x <- 1:3; .$d() }
)
p$a()
原型代码也可以用(1)和(2)的样式编写,但继承不起作用。因为我们在这里不使用它可能无关紧要。
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
4)使x不可见如果你真的想让library(proto)
p <- proto(x = NULL,
b = function(.) { y <- x[2]; x <<- x[-2]; y},
d = function(.) { z <- b(); list(1, z, 3, 4) },
a = function(.) { x <<- 1:3; d() }
)
p$a()
对x
不可见,那么(1)将其更改为:
d
实际上如果L <- local({
x <- NULL
list(b = function() { y <- x[2]; x <<- x[-2]; y },
a = function() { x <<- 1:3; d() })
})
d <- function() { z <- L$b(); list(1, z, 3, 4) }
L$a()
真的想要到达d
它可以像这样访问它;但是,人们真的需要付出努力:
x
对于(2)我们将代码更改为:
environment(a)$x
同样,L <- function() {
x <- NULL
list(b = function() { y <- x[2]; x <<- x[-2]; y },
a = function() { x <<- 1:3; d() })
}
L <- L()
d <- function() { z <- L$b(); list(1, z, 3, 4) }
L$a()
实际上可以访问d
,但同样需要付出相当大的努力:
x
对于(3)我们将代码更改为:
environment(L$a)$x
与(1)和(2)一样,如果我们真的想通过,我们可以从library(proto)
p <- proto(.x = NULL,
b = function(.) { y <- .$.x[2]; .$x <- .$.x[-2]; y},
a = function(.) { .$.x <- 1:3; d() }
)
d = function() { z <- p$b(); list(1, z, 3, 4) }
p$a()
访问x
:
d
但是从对象外部访问点变量会非常明显。
注意:根据评论,您可能更喜欢这样:
p$.x
这清楚地表明e <- local({
self <- environment()
x <- NULL
a <- function() { self$x <- 1:3; d(self) }
b <- function() { y <- x[2]; self$x <- x[-2]; y }
self
})
d <- function(e) { z <- e$b(); list(1, z, 3, 4) }
e$a()
被识别为e
和a
是方法且b
是属性且x
正在行动的对象在通过的环境。它还避免了评论中提到的丑陋的d
。
虽然不太受欢迎,但您可以省略environment(a)$b()
的参数,只需将d
硬编码到e
。请注意,d
可以通过d
访问x
,因此它并非真正隐身,但必须付出额外的努力才能获得资格。这不太受欢迎的原因是,通过将e$x
硬编码到e
,我们将d
与d
联系起来。在这种情况下,将e
放在d
中更合乎逻辑,因为它无论如何都要绑定。另一方面,如果我们将e
传递给e
,则d
与任何特定环境无关。