Как передать аргумент, включенный в '...', в рекурсивный вызов в R - PullRequest
0 голосов
/ 22 ноября 2018

У меня проблемы с передачей аргументов через рекурсивный вызов функции integrate.Вот простой пример, который иллюстрирует мою головоломку.

Рассмотрим функцию foo1, которая требует параметр x и возвращает некоторое преобразование x.Например:

foo1 = function(x, alpha = 1.5) {
  return(x^alpha)
}

Теперь рассмотрим функцию foo2, которая также требует x и возвращает другое преобразование:

foo2 = function(x, y, z) {
  return(x * y * z)
}

Теперь рассмотрим функцию bar, которая возвращаетконечный интеграл (от x до xMax) любой foo -подобной функции, но принимает вектор с несколькими значениями для x:

bar = function(xVals, xMax, ..., fun) {
  return(
    sapply(
      xVals,
      function(xVal) integrate(f = fun, lower = xVal, upper = xMax, ...)$value)
  )
}

Обратите внимание, что параметр foo1alpha будет передано как часть ...:

bar(10:20, 100, alpha = 1.5, fun = foo1)  

и foo2, параметры y и z также будут переданы как часть ...:

bar(10:20, 100, y = 2, z = 4, fun = foo2)

Мне нравится это расположение, потому что другие обязательные параметры для integrate также могут быть переданы как часть ....Например, чтобы изменить значение по умолчанию параметра subdivisions функции integrate, вызываемой bar, можно просто указать:

bar(10:20, 100, y = 2, z = 4, fun = foo2, subdivisions = 200L)

Теперь рассмотрим функцию, которая интегрирует barиз xStart в xEnd, но принимает векторы для нескольких значений для xStart и xEnd.Это может выглядеть так:

integrateBar = function(xStart, xEnd, xMax, ..., fun) {
  aMatrix = cbind(xStart, xEnd)
  result =
    apply(
      aMatrix,
      1,
      function(xRange) integrate(bar, xRange[1], xRange[2], xMax, ..., fun = fun)$value
    )
  return(result)
}

Так что это нормально и довольно просто, пока я не захочу вызвать integrateBar, но изменить значение аргумента subdivisions для функции integrate, вызываемой в bar.Если я использую:

integrateBar(10:20, 40, 100, alpha = 1.5, subdivisions = 200L, fun = foo1)

аргумент subdivisions захватывается и используется функцией integrate в integrateBar и не передается в bar.Такое поведение желательно - иногда.Но иногда я также хочу иметь возможность оставить subdivisions установленным по умолчанию для вызова integrate в integrateBar, но изменить его для вызова integrate в bar.Может кто-нибудь сказать мне элегантный способ сделать последнее?

Я хочу изменить формальности для bar, но я стараюсь не делать это слишком неуклюжим, потому что я тоже хочу вызвать bar напрямую.Было бы очень хорошо, если бы был какой-то трюк R, чтобы аргумент subdivisions был бы проигнорирован первой обнаруженной функцией интегрирования, но доступной для второй.

Лучшее решение, которое у меня есть, - написать оболочкудля integrate, который использует параметр SUBDIVISIONS и вызывает обертку из bar.Если я не получу лучшего ответа, я опубликую это решение.

1 Ответ

0 голосов
/ 22 ноября 2018

Аргумент subdivisions не передается дальше от integrate, потому что это фактический именованный аргумент integrate (только аргументы в ...) будут переданы в f:

> integrate
# function (f, lower, upper, ..., subdivisions = 100L, rel.tol = .Machine$double.eps^0.25, 
#    abs.tol = rel.tol, stop.on.error = TRUE, keep.xy = FALSE, 
#    aux = NULL)

Одним из возможных решений, которое не требует изменения bar, было бы написать оболочку для bar внутри integrateBar.У меня есть два варианта:

Вариант 1: Если вы хотите передать аргументы в ... обоим, внешний и внутренний вызов integrate, это будет:

integrateBar = function(xStart, xEnd, xMax, ..., fun) {

    argsExtra <- list(...)

    # A wrapper function for bar:
    # Note that the argument ... is necessary, although the function does not use
    # it. This is because, we still pass ... to the call to integrate below. 
    CallBarOnFun <- function(xVals, xMax, ...) {
      do.call(bar, c(list(xVals, xMax), argsExtra, list(fun=fun)))
    }

    aMatrix = cbind(xStart, xEnd)
    result =
      apply(
        aMatrix,
        1,
        function(xRange) {
            integrate(CallBarOnFun, xRange[1], xRange[2], xMax, ...)$value
        } 
      )
    return(result)
}

Вариант 2: Если вы хотите передать аргументы в ... только на внутренний вызов:

integrateBar = function(xStart, xEnd, xMax, ..., fun) {
    argsExtra <- list(...)

    CallBarOnFun <- function(xVals, xMax) {
        do.call(bar, c(list(xVals, xMax), argsExtra, list(fun=fun)))
    }

    aMatrix = cbind(xStart, xEnd)
    result =
      apply(
         aMatrix,
         1,
         function(xRange) {
             integrate(CallBarOnFun, xRange[1], xRange[2], xMax)$value
         } 
       )
    return(result)
}
...