Проверьте, используется ли определенная функция внутри вложенной функции в R - PullRequest
4 голосов
/ 17 февраля 2020

Я хочу создать функцию check_sym, которая принимает в качестве аргумента другую функцию f и проверяет, используется ли внутри f указанная c функция.

В частности, я хочу проверить, находится ли внутри f используется нестандартная оценка в виде !! sym.

Я могу сделать это с помощью простого трюка, который превращает тело функции в строку символов, а затем использует регулярное выражение для проверки "!!sym\\(" ,

library(dplyr)
library(purrr)
library(rlang)

check_sym <- function(f) {
  f <- as.list(body(f))
  f <- unlist(as.character(f))
  purrr::some(f, function(x) grepl("!!sym\\(", x))
}

foo <- function(df, x) { 
  select(df, !! sym(x))
}

check_sym(foo)
#> [1] TRUE

Создано в 2020-02-16 с помощью пакета Представить (v0.3.0)

Однако, хотя это возможно, Я ищу способ, который не основывается на символьных строках и регулярных выражениях, а в идеале на некотором методе, который смотрит внутрь функции и «видит» все вызовы функций на более глубоком уровне, который был бы более надежным.

Любые идеи приветствуются.

Окончательное решение на основе принятого ответа:

На основании приведенного ниже ответа MrFlick мое реальное решение следующее:

Я определяю check_syms as:

check_sym <- function(f) {
  grepl("!!sym", paste(all.names(body(f)), collapse = ""))
}

Он правильно определяет функции, которые вызывают функцию на "!! sym", по сравнению с функциями, которые вызывают только, например, paste0("!!sym").

foo <- function(df, x) { 
  select(df, !! sym(x))
}

test_f <- function(x) {
  print(paste0("!!sym", x))
}

check_sym(foo)
#> [1] TRUE

check_sym(test_f)
#> [1] FALSE

Ответы [ 2 ]

5 голосов
/ 17 февраля 2020

В base R мы можем записать вывод body в вектор строк и использовать grepl

check_sym <- function(f)  any(grepl('!!sym(', capture.output(body(f)), fixed = TRUE))

check_sym(foo)
# [1] TRUE 


some_fct <- function(x) print("sym")
check_sym(some_fct)
#[1] FALSE
2 голосов
/ 17 февраля 2020

Вы можете использовать функцию all.names. Он извлекает имена всех переменных / функций, определенных внутри функции

all.names(body(foo))
# [1] "{"      "select" "df"     "!"      "!"      "sym"   
# [7] "x"

Часть проблемы с вашим конкретным примером c заключается в том, что !!sym на самом деле не простая переменная. Это больше похоже на вызов !(!(sym)) парсеру. Материал !! на самом деле не является специальным оператором для синтаксического анализатора R, это всего лишь два вызова оператора отрицания, который Rlang интерпретирует с помощью нестандартной оценки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...