Ответ 1
TL;DR
Когда subset2()
вызывается из subscramble()
,
Значением condition_call
является символ condition
(а не
вызов a >= 4
, который возникает, когда он вызывается напрямую). subset()
's
вызов eval()
выполняет поиск condition
сначала в envir=x
(
data.frame sample_df
). Не найдя его там, он будет искать в enclos=parent.frame()
, где он найдет объект с именем
condition
.
Этот объект является объектом обещания, чей слот выражения
a >= 4
и среда оценки которого .GlobalEnv
. Если
объект с именем a
находится в .GlobalEnv
или дальше по поиску
путь, оценка обещания затем не выполняется с наблюдаемым сообщением
что: Error in eval(expr, envir, enclos) : object 'a' not found
.
Подробное объяснение
Хороший способ узнать, что здесь не так, - вставить
browser()
вызов прямо перед строкой, в которой subset2()
выходит из строя. Таким образом, мы можем назвать это прямо и косвенно (из
в рамках другой функции), и проанализируйте, почему это удается в первом случае и
не работает во втором.
subset2 <- function(x, condition) {
condition_call <- substitute(condition)
browser()
r <- eval(condition_call, x, parent.frame()) ## <- Point of failure
x[r, ]
}
Прямой вызов подмножества2()
Когда пользователь вызывает subset2()
напрямую,
condition_call <- substitute(condition)
присваивает condition_call
объект "вызов"
содержащий неоценимый вызов a >= 4
. Этот вызов передается в
eval(expr, envir, enclos)
, которому в качестве первого аргумента
символ, который оценивается объектом класса call
, name
или
expression
. Пока все хорошо.
subset2(sample_df, a >= 4)
## Called from: subset2(sample_df, a >= 4)
Browse[1]> is(condition_call)
## [1] "call" "language"
Browse[1]> condition_call
## a >= 4
eval()
теперь работает, ищет значения любых символов
содержащейся в expr=condition_call
сначала в envir=x
, а затем (если
необходимо) в enclos=parent.frame()
и окружающих средах. В
в этом случае он находит символ a
в envir=x
(и символ >=
в package:base
) и успешно завершает оценку.
Browse[1]> ls(x)
## [1] "a" "b" "c"
Browse[1]> get("a", x)
## [1] 1 2 3 4 5
Browse[1]> eval(condition_call, x, parent.frame())
## [1] FALSE FALSE FALSE TRUE TRUE
Вызов подмножества2() изнутри subscramble()
Внутри тела subscramble()
, subset2()
вызывается так:
subset2(x, condition)
. Скрытый, этот вызов действительно эквивалентен
до subset2(x=x, condition=condition)
. Поскольку
аргумент (т.е. значение, переданное в аргумент формальный с именем
condition
) является выражением condition
,
condition_call <- substitute(condition)
присваивает condition_call
объект-символ condition
. (Понимание этой точки довольно важно для понимания того, как сбой вложенного вызова.)
Так как eval()
счастлив иметь символ (ака "имя" ) в качестве своего первого
аргумент, еще раз настолько хороший.
subscramble(sample_df, a >= 4)
## Called from: subset2(x, condition)
Browse[1]> is(condition_call)
## [1] "name" "language" "refObject"
Browse[1]> condition_call
## condition
Теперь eval()
переходит в работу, ища неразрешенный символ
condition
. Нет столбца в envir=x
(data.frame sample_df
)
, поэтому он переходит на enclos=parent.frame()
.
Сложные причины, что окружающая среда оказывается оценкой
кадра вызова subscramble()
. Там делает
объект с именем condition
.
Browse[1]> ls(x)
## [1] "a" "b" "c"
Browse[1]> ls(parent.frame()) ## Aha! Here an object named "condition"
## [1] "condition" "x"
Как важно в стороне, оказывается, что в стеке вызовов над средой, из которой был вызван browser()
, есть несколько объектов с именем condition
.
Browse[1]> sys.calls()
# [[1]]
# subscramble(sample_df, a >= 4)
#
# [[2]]
# scramble(subset2(x, condition))
#
# [[3]]
# subset2(x, condition)
#
Browse[1]> sys.frames()
# [[1]]
# <environment: 0x0000000007166f28> ## <- Envt in which `condition` is evaluated
#
# [[2]]
# <environment: 0x0000000007167078>
#
# [[3]]
# <environment: 0x0000000007166348> ## <- Current environment
## Orient ourselves a bit more
Browse[1]> environment()
# <environment: 0x0000000007166348>
Browse[1]> parent.frame()
# <environment: 0x0000000007166f28>
## Both environments contain objects named 'condition'
Browse[1]> ls(environment())
# [1] "condition" "condition_call" "x"
Browse[1]> ls(parent.frame())
# [1] "condition" "x"
Чтобы проверить объект condition
, найденный eval()
(тот, который находится в parent.frame()
, который оказывается рамкой оценки subscramble()
), требует особой осторожности. Я использовал recover()
и pryr::promise_info()
, как показано ниже.
Эта проверка показывает, что condition
является обещанием, чей слот выражения a >= 4
и чья
среда .GlobalEnv
. Наш поиск a
к этому моменту переместился хорошо
прошлое sample_df
(где должно было быть найдено значение a
), поэтому оценка
(если объект с именем a
не найден в .GlobalEnv
или
где-нибудь еще дальше путь поиска).
Browse[1]> library(pryr) ## For is_promise() and promise_info()
Browse[1]> recover()
#
# Enter a frame number, or 0 to exit
#
# 1: subscramble(sample_df, a >= 4)
# 2: #2: scramble(subset2(x, condition))
# 3: #1: subset2(x, condition)
#
Selection: 1
# Called from: top level
Browse[3]> is_promise(condition)
# [1] TRUE
Browse[3]> promise_info(condition)
# $code
# a >= 4
#
# $env
# <environment: R_GlobalEnv>
#
# $evaled
# [1] FALSE
#
# $value
# NULL
#
Browse[3]> get("a", .GlobalEnv)
# Error in get("a", .GlobalEnv) : object 'a' not found
Для еще одного доказательства того, что объект обетования condition
найден
в enclos=parent.frame()
можно указать enclos
где-нибудь еще дальше
путь поиска, так что parent.frame()
пропускается во время оценки condition_call
. Когда человек
что subscramble()
снова терпит неудачу, но на этот раз с сообщением, что
condition
сам не был найден.
## Compare
Browse[1]> eval(condition_call, x, parent.frame())
# Error in eval(expr, envir, enclos) (from #4) : object 'a' not found
Browse[1]> eval(condition_call, x, .GlobalEnv)
# Error in eval(expr, envir, enclos) (from #4) : object 'condition' not found