Я хочу создать функцию 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.
Подход должен работать независимо от того, как вызывается функция:
mutatedplyr::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
