R: как написать оболочку для функции, вызывающей другую функцию

#r

#r

Вопрос:

Я использую функцию caRamel из пакета с тем же именем. Функция принимает другую функцию my_func в качестве аргумента….

 caRamel(
  fn=my_func,
  other_caRamel_parameters...
)
 

my_func — это функция, принимающая уникальный параметр i (по дизайну, обязательный и задается функцией caRamel):

 my_func <- function(i) {
  require(somelib)
  my_path = "C:/myfolder/"
  do things with i
  ...
}
 

По замыслу, нет способа передать дополнительные аргументы функции my_func внутри функции caRamel, и мне приходится жестко кодировать все, например, переменную my_path, например, внутри my_func.

Тем не менее, я хотел бы иметь возможность передавать переменные my_path и другие в качестве параметров в функции caRamel следующим образом:

 caRamel(
  fn=my_func,
  other_caRamel_parameters...,
  my_path="C:/myfolder/", ...
)
 

с:

 my_func <- function(i, my_path) {
  require(somelib)
  my_path = my_path
  do things with i
  ...
}
 

Затем мне стало интересно, можно ли в этом случае написать «оболочку», чтобы дополнительные параметры можно было передавать в my_func? Какие могут быть варианты для достижения этого?

Комментарии:

1. Вам нужна фабрика функций? Я думаю, что это могло бы быть проще с более воспроизводимым примером. В противном случае вы всегда можете использовать ... для передачи других аргументов в любую другую функцию.

2. Привет, кажется, нет. Если я делаю это с функцией caRamel, я получаю ошибку неиспользуемого аргумента: Ошибка в caRamel(… неиспользуемый аргумент (my_path = «…»)…

3. Мы не можем точно сказать, что вы хотите сделать, без какого-либо примера.

Ответ №1:

Мы можем использовать {purrr} и {rlang} в пользовательской myCaRamel функции, которая принимает только многоточие ... в качестве аргумента.

Сначала мы фиксируем точки с rlang::list2(...) помощью .

Затем мы получаем все формалы caRamel функции в виде имен с rlang::fn_fmls_names помощью .

Теперь мы различаем аргументы, которые входят в caRamel функцию, и те, которые входят в вашу пользовательскую функцию.

Мы используем purrr::partial для предоставления аргумента, который входит в ваш my_fun .

Затем мы вызываем исходную caRamel функцию, используя частичную функцию, которую мы создали вместе с аргументами, которые входят в caRamel .

В приведенном ниже примере я rlang::expr в последнем вызове, чтобы показать, что это работает. Пожалуйста, удалите expr() вокруг последнего вызова, чтобы он действительно работал.

Недостатком является то, что каждый аргумент должен иметь правильное имя. Неназванные аргументы и частичное сопоставление не будут работать. Кроме того, если my_fun и caRamel содержат аргументы с похожими именами, они только входят caRamel и не достигают my_fun .

 library(purrr)
library(rlang)
library(caRamel)


my_fun <- function(x, my_path) {
  # use my_path
  # and some x
}

myCaRamel <- function(...) {
  
  dots <- rlang::list2(...)
  
  caramel_args <- rlang::fn_fmls_names(caRamel)
  caramel_args_wo_fun <- caramel_args["func" != caramel_args]
  other_args <- names(dots)[!names(dots) %in% caramel_args]
  
  my_fn <- dots[["func"]]
  
  my_fn <- purrr::partial(my_fn, !!! dots[other_args])
  
  rlang::expr( # just to show correct out put - delete this line 
    caRamel(func = my_fn,
          !!! dots[names(dots) %in% caramel_args_wo_fun])
    )    # delete this line 
                         
}

myCaRamel(func = my_fun,
          my_path = "C:/myfolder/",
          nvar = 10)

#> caRamel(func = my_fn, nvar = 10)
 

Создано 2021-12-11 пакетом reprex (v2.0.1)