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