Каковы преимущества определения и вызова функции внутри другой функции в R?

подход 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))
}

предположим f1 и f3 оба делают те же вычисления и дают тот же результат.

есть значительное преимущества в использовании подхода 1, называя f1(), vs подход 2, вызов f3()?

является ли определенный подход более благоприятным, когда:

  • большие данные передаются и / или из f2?

  • скорость-большая проблема. Е. Г. f1 или f3 вызываются повторно в симуляциях.

(подход 1 кажется распространенным в пакетах, определяя внутри другого)

одно преимущество использования подхода f1 это f2 не будет существовать вне f1 после f1 закончил вызов (и f2 вызывается только в f1 или f3).

2 ответов


преимущества определения 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 (edit: разница на самом деле 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 и снизу. Этот это удобно, так как это может быть единственное время, когда мы используем/нуждаемся в .