在 R 中的另一个函数中定义和调用一个函数有什么好处?

What are the benefits of defining and calling a function inside another function in R?

方法一

f1 <- function(x)
{
    # Do calculation xyz ....

    f2 <- function(y)
    {
        # Do stuff...
        return(some_object)
    }

    return(f2(x))
}

方法二

f2 <- function(y)
{
    # Do stuff...

    return(some_object)
}

f3 <- function(x)
{
    # Do calculation xyz ....
    return(f2(x))
}

假设 f1f3 都进行相同的计算并给出相同的结果。

使用方法 1(调用 f1())与方法 2(调用 f3())相比,是否有任何 显着 优势?

在以下情况下某种方法更有利:

(方法 1 在包中似乎很常见,在另一个包中定义)

使用方法 f1 的一个优点是一旦 f1 完成调用(并且 f2仅在 f1f3) 中调用。

f1 中定义 f2 的好处:

  • f2 仅在 f1 中可见,如果 f2 仅适用于 f1,则有用t export f2 如果你在外面定义它
  • f2 可以访问 f1 内的变量,这可能是好事也可能是坏事:
    • 很好,因为你不必通过函数接口传递变量,你可以使用 <<- 实现记忆等功能。
    • 不好,同样的原因...

缺点:

    每次调用
  • f2都需要重新定义f1,这会增加一些开销(开销不是很大,但肯定有)

数据大小无关紧要,因为 R 不会复制数据,除非它在任何一种情况下被修改。如缺点中所述,在 f1 之外定义 f2 应该会快一点,尤其是当您多次重复一个开销相对较低的操作时。这是一个例子:

> fun1 <- function(x) {
+   fun2 <- function(x) x
+   fun2(x)
+ }
> fun2a <- function(x) x
> fun3 <- function(x) fun2a(x)
> 
> library(microbenchmark)
> microbenchmark(
+   fun1(TRUE), fun3(TRUE)
+ )
Unit: nanoseconds
       expr min    lq median    uq   max neval
 fun1(TRUE) 656 674.5  728.5 859.5 17394   100
 fun3(TRUE) 406 434.5  480.5 563.5  1855   100

在这种情况下,我们节省了 250ns(编辑:差异实际上是 200ns;信不信由你,fun1 额外的一组 {} 又花费了 50ns)。不多,但如果内部函数比较复杂或者你多次重复这个函数,可以加起来。

您通常会使用方法 2。一些例外情况是

  1. 函数闭包:

    f = function() {
        counter = 1
        g = function() {
            counter <<- counter + 1
            return(counter)
        }
     }
     counter = f()
     counter()
     counter()
    

    函数闭包让我们记住状态。

  2. 有时只定义函数很方便,因为它们只在一个地方使用。例如,当使用 optim 时,我们经常调整现有函数。例如,

    pdf = function(x, mu) dnorm(x, mu, log=TRUE)
    f = function(d, lower, initial=0) {
      ll = function(mu) {
        if(mu < lower) return(-Inf)
        else -sum(pdf(d, mu))
      }
      optim(initial, ll)
    }
    
    f(d, 1.5)
    

    ll 函数使用数据集 d 和下限。这很方便,因为这可能是我们唯一一次 use/need ll 函数。

现有答案中提到的一个例子可能是我现在认为在另一个函数的环境中定义一个函数的最有用的好处。简单来说:您可以定义函数而不指定其中使用的所有参数,前提是这些参数在定义函数的环境中的某处定义。函数环境的一个很好的参考当然是:https://adv-r.hadley.nz/environments.html

这种方法可以很方便地将函数中的代码块分解成函数环境中的一堆子函数,从而更清晰地表示代码,而不必写出可能很长的参数列表。

下面一个简单的虚拟示例突出了这一点

f1 <- function(x)
{
  f2 <- function(y)
  {
    # possibly long block of code relevant to the meaning of what `f2` represents
    y + a + b + d
  }

  # might be 10+ variables in special cases
  a <- 10
  b <- 5
  d <- 1

  f2(x)
}

#test:
> f1(100)
[1] 116

如果您使用单独的父环境定义函数,则不能使用此方法:

f3 <- function(x)
{
  a <- 10
  b <- 5
  d <- 1

  f2a(x)
}

f2a <- function(y)
{
  y + a + b + d
}

> f3(100)
Error in f2a(x) : object 'a' not found