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

时间:2015-02-04 21:09:46

标签: r performance function

方法1

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

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

    return(f2(x))
}

方法2

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

    return(some_object)
}

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

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

在使用方法1,调用f1(),与方法2,调用f3()时,是否有重要的优势?

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

  • 大量数据是在f2传入和/或传出的?

  • 速度是个大问题。例如。在模拟中重复调用f1f3

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

使用方法f1的一个好处是f2f1完成调用后不会在f1之外存在(f2仅在f1f3)中调用。

3 个答案:

答案 0 :(得分:9)

f2内定义f1的好处:

  • f2仅在f1中可见,如果f2仅用于f1,则非常有用,但在包名称空间内这是有争议的,因为您不会导出f2如果您在
  • 之外定义它
  • f2可以访问f1中的变量,这可能被视为好事或坏事:
    • 很好,因为你不必通过函数接口传递变量,你可以使用<<-来实现memoization等内容。
    • 不好,原因相同......

缺点:

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

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

> 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)。不多,但如果内部功能更复杂,或者你多次重复这个功能,可以加起来。

答案 1 :(得分:5)

您通常会使用方法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和下限。这很方便,因为这可能是我们使用/需要ll功能的唯一时间。

答案 2 :(得分:0)

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

这种方法可以方便地将函数中的代码块(其中可能需要并在函数体内引用多个变量)分解为函数环境中的一堆子函数,从而更清晰地表示代码,而不必写出可能很长的参数列表。

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

class Cliente:
    def __init__(self):
        self.datos = []

    def opciones(self):
        menu = ['1: Mostrar saldo',
                '2: Ingresar monto',
                '3: Retirar monto',
                '4: Cerrar']
        
        for option in range(len(menu)):
            print(option)

        opc_escoger = int(input('Escriba el número de la opción que desee: '))

        if opc_escoger == 1:
            self.mostrar()
        elif opc_escoger == 2:
            self.Ingresar()
        elif opc_escoger == 3:
            self.retirar()
        elif opc_escoger == 4:
            print('Saliendo...')
            exit()
        elif opc_escoger == 5:
            self.registrarse

        self.opciones()

    def registrarse(self):
        print('REGISTRO')
        nombre = input('Ingrese su nombre de usuario: ')
        idnum = input('Ingrese su número de documento de identificación: ')
        email = input('Ingrese su dirección de correo electrónico: ')
        self.datos.append({'nombre':nombre, 'idnum':idnum, 'email':email, 'monto': 0})

    def ingresar(self):
        print('INGRESAR')

        nombre = input('Ingrese su nombre de usuario: ')
        idnum = input('Ingrese su número de documento de identificación: ')
        email = input('Ingrese su dirección de correo electrónico: ')

        for x in range(len(self.datos)):
            if self.datos[x]['nombre'] == nombre and self.datos[x]['idnum'] == idnum and self.datos[x]['email'] == email:
                print('Dinero actual: ', self.datos[x]['monto'])
                monto = input('¿Cuál es el monto a ingresar?: ')
                self.datos[x]['monto':monto]
            else:
                print('Los datos ingresados no coinciden con los de un usuario registrado')
                self.registrarse()


#Here's some more code for the other methods, but are pretty similar to the 'ingresar' method

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

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