Я хочу создать функцию myfun, которую можно использовать только внутри другой функции, в моем случае dplyrs mutate или summarise. Я больше не хочу полагаться на dplyrс внутренностями (например mask$…).
Я придумал быстрый и грязный обходной путь: функция search_calling_fn, которая проверяет все имена функций в стеке вызовов и ищет определенный шаблон в вызывающих функциях.
search_calling_fn <- function(pattern) { call_st <- lapply(sys.calls(), `[[`, 1) res <- any(unlist(lapply(call_st, function(x) grepl(pattern, x, perl = TRUE)))) if (!res) { stop(«`myfun()` must only be used inside dplyr::mutate or dplyr::summarise») } else { return() } }
Это работает, как и ожидалось, как показано в двух приведенных ниже примерах (dplyr = 1.0.0).
library(dplyr) myfun <- function() { search_calling_fn(«^mutate|^summarise») NULL } # throws as expected no error mtcars %>% mutate(myfun()) myfun2 <- function() { search_calling_fn(«^select») NULL } # throws as expected an error mtcars %>% mutate(myfun2())
В этом подходе есть одна лазейка: myfun можно вызывать из функции с похожим именем, которая не является функцией dplyr. Интересно, как я могу проверить, из какого пространства имен приходит функция в моем стеке вызовов. rlang имеет функцию call_ns, но она будет работать только в том случае, если функция явно вызывается с package::…. Кроме того, при использовании mutate в стеке вызовов есть mutate_cols внутренняя функция и mutate.data.frame метод S3 — кажется, что оба делают получение пространства имен еще более сложным.
С другой стороны, мне интересно, есть ли лучший, более официальный подход для достижения того же результата: разрешить вызов myfun только в пределах dplyrs mutate или summarise.
Подход должен работать независимо от того, как вызывается функция:
- mutate
- dplyr::mutate
Дополнительное примечание
После обсуждения ответа @r2evans я понимаю, что решение должно пройти следующий тест:
library(dplyr) myfun <- function() { search_calling_fn(«^mutate|^summarise») NULL } # an example for a function masking dplyr’s mutate mutate <- function(df, x) { NULL } # should throw an error but doesn’t mtcars %>% mutate(myfun())
Таким образом, функция проверки должна не только смотреть на стек вызовов, но и пытаться увидеть, из какого пакета исходит функция в стеке вызовов. Интересно, что отладчик RStudios показывает пространство имен для каждой функции в стеке вызовов, даже для внутренних функций. Интересно, как он это делает, ведь environment(fun)) работает только с экспортированными функциями.
Придирка: вам не хватает закрытия } в конце функции search_calling_fn. — person TimTeaFan schedule 06.07.2020
Спасибо, что заметили это! Я исправил это. — person TimTeaFan schedule 06.07.2020
Связано: 1 и 2. — person TimTeaFan schedule 06.07.2020
Ваш пример кода mutate никогда не даст сбоев, потому что x ленив; поскольку он никогда не используется, он никогда не реализуется, поэтому myfun никогда не вызывается. … но я понимаю вашу точку зрения, getAnywhere в моем ответе слишком нетерпелив. — person TimTeaFan schedule 08.07.2020
Источник: