deparse (substitute ()) обычно возвращает имя функции, но код функции при вызове внутри цикла for

Я немного удивлен поведением R в очень конкретном случае. Скажем, я определяю функцию square, которая возвращает квадрат своего аргумента, например:

square <- function(x) { return(x^2) }

Я хочу вызвать эту функцию в другой функции, и я также хочу отображать ее имя, когда я это сделаю. Я могу сделать это с помощью deparse(substitute()). Однако рассмотрим следующие примеры:

ds1 <- function(x) {
  print(deparse(substitute(x)))
}

ds1(square)
# [1] "square"

Это ожидаемый результат, так что все в порядке. Однако, если я передаю функцию, заключенную в список, и обработаю ее с помощью цикла for, произойдет следующее:

ds2 <- function(x) {
  for (y in x) {
    print(deparse(substitute(y)))
  }
}

ds2(c(square))
# [1] "function (x) "   "{"               "    return(x^2)" "}"  

Может ли кто-нибудь объяснить мне, почему это происходит и как я могу предотвратить это?


person A. Stam    schedule 20.12.2017    source источник


Ответы (1)


Как только вы используете x внутри своей функции, она оценивается, поэтому она «перестает быть (неоцененным) выражением» и «начинает быть его результирующими значениями (оцениваемым выражением)». Чтобы предотвратить это, вы должны захватить x с помощью substitute, прежде чем использовать его в первый раз.

Результат substitute - это объект, который вы можете запросить, как если бы это был список. Итак, вы можете использовать

x <- substitute(x)

а затем x[[1]] (имя функции) и x[[2]] и последующие (аргументы функции)

Итак, это работает:

ds2 <- function(x) {
    x <- substitute(x)
    # you can do `x[[1]]` but you can't use the expression object x in a
    # for loop. So you have to turn it into a list first
    for (y in as.list(x)[-1]) {
        print(deparse(y))
    }
}
ds2(c(square,sum))
## [1] "square"
## [1] "sum"
person akraf    schedule 20.12.2017
comment
Потрясающе, спасибо. По-видимому, технический термин для этого - «объект обещания». Хотя x является обещанием, его элементы (при назначении y) больше не являются обещаниями, и поэтому substitute возвращает другое значение. - person A. Stam; 20.12.2017
comment
Как ни странно для моих целей (запишите имя переменной и поместите его в качестве заголовка после некоторых вычислений в tidyverse), deparse не нужен. В чем разница между заменой (deparse (var)) и просто заменой (var)? Также бонус в: это все еще лучшая практика с новым NSE от tidyverse? - person JoeTheShmoe; 08.10.2020
comment
@JoeTheShmoe substitute() возвращает объект вызова, а deparse() превращает объект вызова в строку. Возможно, что если вы предоставите результат substitute() вашей функции генерации заголовков, она просто вызовет сам deparse(), но я точно не знаю. И в tidyverse вам редко понадобится substitute(), см. Виньетку Программирование с dplyr для всех способы реагирования на заголовки столбцов и т. д., предоставленные пользователем - person akraf; 09.10.2020
comment
Хм интересно. И да, я прочитал ту виньетку, которая привела меня сюда. Я не мог найти прямого места, где бы они говорили об обработке ввода как символа. Старая виньетка, я думаю, была с какой-то странной смесью enquo () и sym (), может быть, но я ее больше не вижу, но, может быть, я просто где-то там ее пропустил - person JoeTheShmoe; 09.10.2020