curve3d не может найти локальную функцию "fn"
Я пытаюсь использовать функцию curve3d
в emdbook
-package, чтобы создать контурный график функции, определенной локально внутри другой функции, как показано в следующем минимальном примере:
library(emdbook)
testcurve3d <- function(a) {
fn <- function(x,y) {
x*y*a
}
curve3d(fn(x,y))
}
Неожиданно это порождает ошибку
> testcurve3d(2)
Error in fn(x, y) : could not find function "fn"
тогда как одна и та же идея отлично работает с более базовой функцией curve
base
-package:
testcurve <- function(a) {
fn <- function(x) {
x*a
}
curve(a*x)
}
testcurve(2)
Вопрос в том, как curve3d
может быть переписана так, что она ведет себя так, как ожидалось.
Ответы
Ответ 1
Вы можете временно attach
среду функций к пути поиска, чтобы заставить ее работать:
testcurve3d <- function(a) {
fn <- function(x,y) {
x*y*a
}
e <- environment()
attach(e)
curve3d(fn(x,y))
detach(e)
}
Анализ
Проблема возникает из этой строки в curve3d
:
eval(expr, envir = env, enclos = parent.frame(2))
На данный момент мы оказываемся на 10 кадров в глубину, а fn
определяется в parent.frame(8)
. Поэтому вы можете отредактировать строку в curve3d
чтобы использовать ее, но я не уверен, насколько это curve3d
. Возможно, parent.frame(sys.nframe()-2)
может быть более надежным, но поскольку ?sys.parent
предупреждает, что могут произойти некоторые странные вещи:
Строго, sys.parent и parent.frame относятся к контексту родительской интерпретируемой функции. Таким образом, внутренние функции (которые могут или не могут задавать контексты и поэтому могут или не могут отображаться в стеке вызовов) могут не учитываться, а методы S3 также могут делать удивительные вещи.
Остерегайтесь эффекта ленивой оценки: эти две функции смотрят на стек вызовов в момент их оценки, а не на момент их вызова. Передача вызовов им как аргументов функции вряд ли будет хорошей идеей.
Ответ 2
Решение eval-parse обходит некоторые опасения по поводу переменной сферы. Это передает значение как переменной, так и функции напрямую, а не передает имена переменных или функций.
library(emdbook)
testcurve3d <- function(a) {
fn <- eval(parse(text = paste0(
"function(x, y) {",
"x*y*", a,
"}"
)))
eval(parse(text = paste0(
"curve3d(", deparse(fn)[3], ")"
)))
}
testcurve3d(2)
![result]()
Ответ 3
Я нашел другое решение, которое мне не очень нравится, но, возможно, это поможет вам.
Вы можете создать функцию fn
как объект call
и eval this в curve3d
:
fn <- quote((function(x, y) {x*y*a})(x, y))
eval(call("curve3d", fn))
Внутри другой функции, непрерывная проблема существует, должна быть в глобальной окружающей среде, но это можно исправить с a
substitute
.
Пример:
testcurve3d <- function(a) {
fn <- substitute((function(x, y) {
c <- cos(a*pi*x)
s <- sin(a*pi*y/3)
return(c + s)
})(x, y), list(a = a))
eval(call("curve3d", fn, zlab = "fn"))
}
par(mfrow = c(1, 2))
testcurve3d(2)
testcurve3d(5)
![enter image description here]()