rlang: Ошибка: не удается преобразовать функцию в строку

Я создал функцию для преобразования имени функции в строку. Версия 1 func_to_string1 работает хорошо, а версия 2 func_to_string2 не работает.

func_to_string1 <- function(fun){
    print(rlang::as_string(rlang::enexpr(fun)))
}

func_to_string2 <- function(fun){
    is.function(fun)
    print(rlang::as_string(rlang::enexpr(fun)))
}

func_to_string1 работает:

> func_to_string1(sum)
[1] "sum"

func_to_string2 не работает.

> func_to_string2(sum)
 Error: Can't convert a primitive function to a string
Call `rlang::last_error()` to see a backtrace 

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

Мои вопросы: почему это происходит и есть ли лучший способ преобразовать имя функции в строку?

Любая помощь приветствуется, спасибо!


person yusuzech    schedule 14.08.2019    source источник


Ответы (3)


Это не полный ответ, но я не думаю, что он подходит для комментария.

В R есть механизм, называемый передачей по обещанию, посредством которого формальные аргументы функции являются ленивыми объектами (обещаниями), которые оцениваются только при их использовании. Даже если вы не выполняли никакого присваивания, вызов is.function использует аргумент, поэтому обещание «заменяется» результатом его оценки.

Тем не менее, на мой взгляд, это похоже на несоответствие в rlang *, особенно с учетом ответа Кори, который подразумевает, что R все еще может найти объект обещания даже после того, как заданный параметр был использован; однако механизм для этого может не быть частью общедоступного API R.

* РЕДАКТИРОВАТЬ: см. Комментарии.

Тем не менее, вы можете рассматривать _3 _ / _ 4 _ / _ 5_ как base::missing в том смысле, что вы должны использовать их только с параметрами, которые вы вообще не использовали в теле функции.

person Alexis    schedule 14.08.2019
comment
Это не противоречие в rlang. После принудительного выполнения обещания у него больше нет среды, а это значит, что мы не можем создать из него запрос. Мы решили сделать enexpr() совместимым с enquo(). - person Lionel Henry; 15.08.2019
comment
Если вам не нужны квазиквотация или квотирование, тогда использование substitute() имеет смысл. - person Lionel Henry; 15.08.2019
comment
@LionelHenry Я понимаю, я думаю, это имеет смысл, хотя документация могла бы быть более конкретной, поскольку в ней говорится, что enexpr соответствует substitute, но enexpr может возвращать не-выражения, если данное обещание уже было оценено. Кроме того, в быстром тесте enquo действительно создает вопрос, хотя и с пустым окружением. - person Alexis; 15.08.2019
comment
В некоторых случаях substitute() может также возвращать не-выражения, потому что компилятор байт-кода разворачивает константы из обещаний. - person Lionel Henry; 16.08.2019

Может быть, использовать это вместо этого?

func_to_string2 <- function(fun){
  is.function(fun)
  deparse(substitute(fun)) 
  #print(rlang::as_string(rlang::enexpr(fun)))
}
> func_to_string2(sum)
[1] "sum"
person cory    schedule 14.08.2019

Этот вопрос поднимает интересный момент о ленивых оценках.

Аргументы R вычисляются лениво, то есть аргументы не оцениваются до тех пор, пока они не потребуются.

Лучше всего это понять в книге Advanced R, в которой есть следующий пример:

f <- function(x) {
  10
}
f(stop("This is an error!"))

результат равен 10, что удивительно, потому что x никогда не вызывается и, следовательно, никогда не оценивается. Мы можем принудительно вычислить x, используя force()

f <- function(x) {
  force(x)
  10
}
f(stop("This is an error!"))

Это ведет себя так, как ожидалось. На самом деле нам даже не нужен force() (хотя было бы хорошо быть явным).

f <- function(x) {
  x
  10
}
f(stop("This is an error!"))

Вот что происходит с вашим звонком здесь. Функция sum, которая изначально является символом, оценивается без аргументов при вызове is.function(). Фактически, даже это не удастся.

func_to_string2 <- function(fun){
  fun
  print(rlang::as_string(rlang::ensym(fun)))
}

В целом, я считаю, что лучше всего использовать enexpr() в самом начале функции.

Источник:

http://adv-r.had.co.nz/Functions.html

person Sada93    schedule 14.08.2019