【问题标题】:Using 'get' Inside Nested Functions on Local Variables在局部变量的嵌套函数中使用“get”
【发布时间】:2014-06-20 23:39:32
【问题描述】:

我从来没有完全理解嵌套函数和通过引用传递参数。我的策略通常是在子函数中执行类似get('variabletopassbyreference') 的操作来完成此操作。

到目前为止,我一直在将全局变量传递给函数,并且效果很好。今天我尝试在函数内创建局部变量,然后将它们传递给该函数内的嵌套函数,但失败了。我无法让get 工作。我还尝试修改 posinherits 但无济于事。

我在网上找不到确切的答案。如果我可以让这个构造工作,那么这就是我的偏好,因为我有很多其他的函数,我已经以类似的方式编写了代码。如果我根本不应该这样做而应该做其他事情,那么这些信息也会受到赞赏。

下面是一个例子-

test1 <- function(a1,b1) {

  # cat(ls()) # a1 b1
  # cat(ls(pos = 1)) # c test1 test2

  testvalue <- get('c') * get(a1, inherits = TRUE) * get(b1)

  testvalue

}

test2 <- function() {

  a = 1
  b <- 2
  # cat(ls()) # a b
  test1('a','b')

}

c = 3
test2()

我收到以下错误 -

Error in get(a1, inherits = TRUE) : object 'a' not found 

更通用的示例 -

a = 0

test1 <- function(a1,b1) {

  # cat(ls()) # a1 b1
  # cat(ls(pos = 1)) # c test1 test2

  testvalue <- get('c') * a1 * b1

  assign(x = 'a', value = 2.5)
  assign(x = 'a', value = 3.5, envir = parent.frame())
  assign(x = 'a', value = 4.5, envir = .GlobalEnv)
  cat(a)
  cat(' - value of a local within test1\n')
  testvalue

} 

test2 <- function() {

  a = 1
  b <- 2
  # cat(ls()) # a b

  cat(a)
  cat(' - value of a local within test2 before test1 called\n')
  test1(a1 = a, b1 = b)
  cat(a)
  cat(' - value of a local within test2 after test1 called\n')

}
cat(a)
cat(' - value of a global before test 2 \n')
c = 3
test2()

cat(a)
cat(' - value of a global after test 2 \n')

【问题讨论】:

  • 您为什么对通过引用传递感兴趣?
  • 我假设按值传递会在内存中创建一个副本。在处理非常大的数据集时,这是我买不起的奢侈品。很少,我也需要在函数本身中修改对象。
  • 事实并非如此。仅当您尝试修改参数时才会进行复制。这里详细讨论:stackoverflow.com/questions/15759117/…
  • 那是很多文字,我要阅读它,但在我开始这段旅程之前 - 所以在上面的例子中,如果我也做了类似assign(a1, 3) 的事情,那么它会创建一个副本,但是否则就继续引用原来的a?然后我可以使用assign 中的envir 参数来修改全局变量本身。
  • 恐怕assignget 在同一个篮子里。这是一种可怕的方法。您现在是否正在尝试修改函数范围之外的对象?如果是这种情况,请让您的示例更通用,以便我可以帮助提供替代方案。也许你会对proto 包感兴趣,它提供了一个简单的面向对象(通过引用)的设计。

标签: r function nested


【解决方案1】:

另外,传递变量所在的环境。注意parent.frame()指的是调用者当前运行实例中的环境。

test1 <- function(a1, b1, env = parent.frame()) {

  a <- get(a1, env)
  b <- get(b1, env)
  c <- get('c', env)

  testvalue <- c * a * b

  testvalue

}

c <- 3
test2() # test2 as in question
## 6

这里abenvc 不在env 中,但它在envget 的祖先中也可以通过ancenstors 查找。

添加请注意,R 公式可用于通过环境传递变量名称:

test1a <- function(formula) {
    v <- all.vars(formula)
    values <- sapply(v, get, environment(formula))
    prod(values)
}

test2a <- function() {
    a <- 1
    b <- 2
    test1a(~ a + b + c)
}

c <- 3
test2a()
## 6

修订:已更正。添加了评论。添加了有关公式的信息。

【讨论】:

  • 第一部分是我要找的。我尝试在test2 的环境中search 并看到.GlobalEnv,当我执行parent.frame() 时,我得到&lt;environment: R_GlobalEnv&gt;。他们不一样吗?为什么我的代码不起作用?
  • parent.frame()在修改后的test1中被调用时,它指的是其调用者中的环境,即test2当前运行实例中的环境,而不是全局环境。已在答案顶部添加评论。
  • 谢谢。我接受了弗洛德尔的回答,因为它提供了更多信息,因此我只能给你一个 +1。
【解决方案2】:

既然你在问,这对我来说绝对是一个糟糕的设计。推荐的方法是坚持 R 的值传递方式。并尽可能让每个函数都将它使用的所有内容作为参数:

test1 <- function(a1, b1, c1 = 1) {
   testvalue <- c1 * a1 * b1   
   testvalue
}

test2 <- function(cc = 1) {
   a <- 1
   b <- 2
   test1(a1 = a, b1 = b, c1 = cc)
}

cc <- 3
test2(cc = cc)

(我将c替换为cc,因为它是函数的名称,因此用作变量名是个坏主意。)

一种不太可接受但可能更接近您所拥有的方法是不将所有参数传递给您的函数,让 R 在调用堆栈中查找它们:

test1 <- function(a1, b1) {
   testvalue <- cc * a1 * b1   
   testvalue
}

test2 <- function() {
   a <- 1
   b <- 2
   test1(a, b)
}

cc <- 3
test2()

如果由于某种原因第一种方法对您不起作用,请解释原因,以便我有机会说服您。这是推荐的 R 编程方式。


在讨论和您的编辑之后,我建议您查看proto 包作为getassign 的替代方案。本质上,proto 对象是环境,所以base R 没有什么不能做的,但它有助于让事情变得更干净:

test1 <- function(x) {
   testvalue <- x$c * x$a * x$b
   x$a <- 3.5
   testvalue
}

test2 <- function(x) {
   x$a <- 1
   x$b <- 2
   cat(x$a, '\n')
   test1(x)
   cat(x$a, '\n')
}

library(proto)
x <- proto(c = 3)
test2(x)

从编程的角度来看,test1test2 是具有副作用的函数(它们修改对象x)。请注意,这是一种冒险的做法。

或者更好的方法是让test1test2 成为一个类的方法,那么如果他们修改他们正在运行的实例是可以接受的:

x <- proto() # defines a class

x$test1 <- function(.) {
   testvalue <- .$c * .$a * .$b
   .$a <- 3.5
   testvalue
}

x$test2 <- function(.) {
   .$a <- 1
   .$b <- 2
   cat(.$a, '\n')
   .$test1()
   cat(.$a, '\n')
}

library(proto)
y <- x$proto(c = 3)  # an instance of the class
y$test2()

如果您对使用第三方包(proto)不感兴趣,那么请查看 R 对构建类的支持(setClasssetRefClass)。我确实相信根据您的规格使用面向对象的设计是正确的方法。

【讨论】:

  • 参考类呢?这些不是大致相当于 proto 包,并且是标准 R 的一部分吗?
  • +1。这看起来很有趣。我没有得到. 的东西,但我想这是一些语法细节。谢谢!但是,我仍然不明白为什么我的原始代码不起作用。如果你能告诉我,那么复选标记就是你的了。
  • . 类似于其他 OO 编程语言中的 self 变量。它指的是实例。我认为你会得到一个错误,因为test1 是在全局环境中定义的,所以get 将首先在test1 中查找a,然后在全局环境中查找,而不是在test2 中查找,除非你告诉它。例如,如果 test1 是在 test2 中定义的,或者在您的 get 调用中添加 envir = parent.frame() 之后,请查看区别。
  • 我认为这是它看起来的顺序 - 当前环境,那个父级(从哪里调用 current),那个父级的父级(从哪里调用 current 的父级),等等等等。这对我来说似乎比你描述的更直观(我检查过并且是正确的)。
猜你喜欢
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多