首页 > 解决方案 > 什么时候值得在 R 函数中使用`remove`?

问题描述

在决定是否remove将不会在函数中再次使用的变量时,我应该考虑哪些因素?

这是一个点头的例子:

DivideByLower <- function (a, b) {
  if (a > b) {
    tmp <- a
    a <- b
    b <- tmp
    remove(tmp) # When should I include this line?
  }

  # Return:
  a / b
}

我知道tmp当函数完成执行时它将被删除,但我是否应该担心提前删除它?

标签: rperformancefunction

解决方案


来自Hadley Wickham 的高级 R

在某些语言中,您必须显式删除未使用的对象才能返回它们的内存。R 使用了另一种方法:垃圾收集(或简称 GC)。当不再使用对象时,GC 会自动释放内存。它通过跟踪有多少名称指向每个对象来做到这一点,并且当没有名称指向一个对象时,它会删除该对象。

在您描述垃圾收集的情况下,将释放内存。

如果您的函数的输出是另一个函数,在这种情况下,Hadley 将这些函数分别命名为函数工厂制造函数,在函数工厂主体中创建的变量将在制造函数的封闭环境中可用,并且内存不会被释放。

更多信息,仍然在 Hadley 的书中,可以在关于函数工厂的章节中找到。

function_factory <- function(x){
  force(x)
  y <- "bar"
  fun <- function(z){
    sprintf("x, y, and z are all accessible and their values are '%s', '%s', and '%s'",
            x, y, z)
  }
  fun
}

manufactured_function <- function_factory("foo")
manufactured_function("baz")
#> [1] "x, y, and z are all accessible and their values are 'foo', 'bar', and 'baz'"

reprex 包(v0.3.0)于 2019 年 7 月 8 日创建

在这种情况下,如果您想控制封闭环境中可用的变量,或者确保您不会弄乱您的记忆,您可能希望删除不必要的对象,或者像您所做的那样使用rm/ remove,或者像我倾向于喜欢,包裹在一个on.exit声明中。

我可能会使用 rm 的另一种情况是,如果我想从父环境访问变量而不会有在函数内部被覆盖的风险,但在这种情况下,通常可以使用eval.parent.

y <- 2
z <- 3
test0 <- function(x, var){
  y <- 1
  x + eval(substitute(var))
}

# opps, the value of y is the one defined in the body
test0(0, y)
#> [1] 1
test0(0, z)
#> [1] 3

# but it will work using eval.parent :
test1 <- function(x, var){
  y <- 1
  x + eval.parent(substitute(var))
}
test1(0, y)
#> [1] 2
test1(0, z)
#> [1] 3

# in some cases (better avoided), it can be easier/quick and dirty to do something like :
test2 <- function(x, var){
  y <- 1
  # whatever code using y
  rm(y)
  x + eval(substitute(var))
}
test2(0, y)
#> [1] 2
test2(0, z)
#> [1] 3

reprex 包(v0.3.0)于 2019 年 7 月 8 日创建


推荐阅读