Как я могу определить, задан ли аргумент функции как выражение, не оценивая его?

#r #expression #eval

#r #выражение #вычисление

Вопрос:

Вот простая функция, которая принимает выражение в качестве аргумента.

 f <- function(expr) {
  expr <- substitute(expr)
  eval(expr)
}
 

(На практике мне также нужно немного манипулировать выражением в функции и может оценивать выражение в какой-либо другой среде, где оно имеет смысл)

Он отлично работает, когда мы вызываем

 f(1 1)
 

путем прямого указания неоценимого аргумента.

Однако мне f также нужно работать, например, когда я предоставляю явно определенное выражение снаружи, например,

 q <- quote(1 1)
f(q)
 

f(expr) необходимо избегать замены expr и возвращать значение выражения. Однако приведенный выше код не работает, потому что он приводит к самому выражению, а не к 2.

Итак, вопрос в следующем: как я могу определить, задан ли аргумент как выражение, не оценивая его в текущей среде?

Ответ №1:

Возможно, вы хотели бы проверить, передано ли вам «имя», а не «вызов»

 f <- function(expr) {
    expr <- substitute(expr)
    if(is.name(expr)) {
        expr <- eval(expr, parent.frame())
    }
    eval(expr)
}


f(1 1)
# [1] 2

q<-quote(1 1)
f(q)
# [1] 2
 

Комментарии:

1. Мне нравится этот гораздо более чистый подход. 1

2. Почему мы не можем просто сделать: f <- function(expr) {expr <- substitute(expr); eval(expr, parent.frame())} (исключить однострочную функцию, как это указано в комментариях)?

3. @TylerRinker Этот метод, безусловно, правильно оценит выражение, я делаю дополнительный шаг, потому что OP сказал, что они могут захотеть изменить выражение. Таким образом, после оператора if вы должны иметь возможность изменять код, который будет оцениваться.

4. Крошечный выбор nit: вы никогда не должны использовать == with class, так как он может возвращать несколько значений (также я думаю, что это сбивает с толку, когда объект не является S3). Здесь вы можете использовать is.name

5. Также это приведет к сбою, если вы предоставите цитируемый объект напрямую. По сути, это плохая идея пытаться заставить функцию выполнять как стандартную, так и нестандартную оценку.

Ответ №2:

Вероятно, есть способы получше, но вот один подход:

 f <- function(expr) {
    mess <- suppressWarnings(try(expr, silent = TRUE))
    if (inherits(mess, "try-error")) {
        expr <- substitute(expr)
    }

    eval(expr)
}

f(1 1)
## [1] 2

q <- quote(1 1)
f(q)
## [1] 2
 

Этот бит расширяет его до символьных векторов:

 f <- function(expr) {
    mess <- suppressWarnings(try(expr, silent = TRUE))
    if (inherits(mess, "try-error")) {
        expr <- substitute(expr)
    }

    if (is.character(expr)) return(eval(parse(text=expr)))
    eval(expr)
}

f("1   1")
## [1] 2
 

Я бы рекомендовал прочитать этот фрагмент ЗДЕСЬ из расширенного R Хэдли