在局部变量的嵌套函数中使用'get'

时间:2014-05-04 17:41:32

标签: r function nested

我从来没有完全了解嵌套函数并通过引用传递参数。我的策略通常是在子函数中执行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')

2 个答案:

答案 0 :(得分:4)

此外,传递变量所在的环境。请注意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

ab位于env c的{​​{1}}不在env,但它位于env和{{的祖先之中1}}也可以通过观察者来看。

ADDED 请注意,R公式可用于在环境中传递变量名称:

get

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

答案 1 :(得分:3)

既然你问,这对我来说绝对是一个糟糕的设计。推荐的方法是坚持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)的支持。我相信使用面向对象的设计是适合您规范的正确方法。