promesa ya en evaluación: referencia recursiva de argumento predeterminado o problemas anteriores?

143

Aquí está mi código R. Las funciones se definen como:

f <- function(x, T) {
  10 * sin(0.3 * x) * sin(1.3 * x ^ 2) + 0.001 * x ^ 3 + 0.2 * x + 80
}

g <- function(x, T, f=f) {
  exp(-f(x) / T)
}

test <- function(g=g, T=1) { 
  g(1, T)
}

El error de ejecución es:

> test ()
Error en test ():
promesa ya bajo evaluación: ¿referencia de argumento predeterminada recursiva o problemas anteriores?

Si sustituyo la definición de fen de g, entonces el error desaparece.

Me preguntaba cuál fue el error? ¿Cómo corregirlo si no sustituye la definición de fen g? ¡Gracias!


Actualizar:

¡Gracias! Dos preguntas:

(1) si la función testrequiere un argumento adicional f, ¿agregará algo como test <- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) }? En casos con más recurrencias, ¿es una práctica buena y segura agregar más ? ?

(2) si fes un argumento no funcional, por ejemplo , g <- function(x, T, f=f){ exp(-f*x/T) }y test <- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }, ¿usará el mismo nombre para argumentos no funcionales formales y reales es una práctica buena y segura o puede causar algún problema potencial?

Tim
fuente

Respuestas:

159

Los argumentos formales de la forma x=xcausan esto. Eliminando las dos instancias donde ocurren obtenemos:

f <- function(x, T) {
   10 * sin(0.3 * x) * sin(1.3 * x^2) + 0.001 * x^3 + 0.2 * x + 80 
}

g <- function(x, T, f. = f) {  ## 1. note f.
   exp(-f.(x)/T) 
}

test<- function(g. = g, T = 1) {  ## 2. note g.
   g.(1,T) 
}

test()
## [1] 8.560335e-37
G. Grothendieck
fuente
2
¡Gracias! Dos preguntas (1) si test de función toma un argumento adicional para f , ¿agregará algo como test <- function (g. = G, T = 1, f .. = f) {g. (1, T, f. = f ..)} ? En casos con más recurrencias, ¿es una práctica buena y segura agregar más ? ? (2) si f es un argumento sin función, por ejemplo g <- function (x, T, f = f) {exp (-f x / T)} * y test <- function (g. = G, T = 1, f = f) {g. (1, T, f = f.)} , ¿Utilizará el mismo nombre para argumentos no funcionales formales y reales como una práctica buena y segura o puede causar algún problema potencial?
Tim
16
¿Alguna otra solución? Estoy pasando algunos argumentos en el fondo de la cadena de funciones (aproximadamente 5 niveles), y esta solución puede ser .....cumbersome. :)
Roman Luštrik
2
@ RomanLuštrik Si está pasando los argumentos hacia abajo y puede ignorar algunos de ellos de manera segura, considere el uso de puntos suspensivos ...o una lista para pasar los argumentos por la cadena de funciones. Es mucho más flexible (para bien y para mal) que predefinir todo. Es posible que necesite agregar algunas comprobaciones para asegurarse de que sus argumentos originales en las elipses (o lista) sean razonables.
russellpierce
2
Otra opción aquí es intentar explícitamente encontrar los argumentos en un marco principal, evitando el forzamiento accidental de la promesa activa, por ejemplo get("f", envir = parent.frame()).
Kevin Ushey
1
El único requisito es que no use el mismo nombre en el lado izquierdo y derecho. Aparte de eso, es solo estilo.
G. Grothendieck
13

Si especifica el contexto de evaluación de argumentos, evita el problema del mismo nombre:

f <- function(x) {
  10 * sin(0.3 * x) * sin(1.3 * x ^ 2) + 0.001 * x ^ 3 + 0.2 * x + 80
}
g <- function(x, t=1, f=parent.frame()$f) {
  exp(-f(x) / t)
}
test <- function(g=parent.frame()$g, t=1) { 
  g(1,t)
}
test()
[1] 8.560335e-37
xm1
fuente
2
Se trata de una mejor manera, creo que especifica el medio ambiente es más clara
cloudscomputes
1

Me gusta la respuesta de G. Grothendieck , pero me preguntaba si es más simple en su caso no incluir nombres de funciones en los parámetros de funciones, como este:

f <- function(x, T) {
  10 * sin(0.3 * x) * sin(1.3 * x^2) + 0.001 * x^3 + 0.2 * x + 80 
}
g <- function(x, T) {
  exp(-f(x)/T) 
}
test<- function(T = 1) {
  g(1,T)
}
test()
## [1] 8.560335e-37
t4x0n
fuente
1

Como ya se mencionó, el problema proviene de tener un argumento de función definido como sí mismo. Sin embargo, quiero agregar una explicación de por qué esto es un problema porque la comprensión me llevó a una forma más fácil (para mí) de evitar el problema: solo especifique el argumento en la llamada en lugar de la definición.

Esto no funciona:

x = 4
my.function <- function(x = x){} 
my.function() # recursive error!

pero esto funciona:

x = 4
my.function <- function(x){} 
my.function(x = x) # works fine!

Los argumentos de función existen en su propio entorno local.

R busca variables primero en el entorno local, luego en el entorno global. Así es como dentro de una función una variable puede tener el mismo nombre que una variable en el entorno global, y R usará la definición local.

Tener definiciones de argumentos de función que formen su propio entorno local es la razón por la cual puede tener valores de argumento predeterminados basados ​​en otros valores de argumento, como

my.function <- function(x, two.x = 2 * x){}

Por eso no puede DEFINIR una función, my.function <- function(x = x){}pero puede LLAMAR la función usando my.function(x = x). Cuando define la función, R se confunde porque encuentra el argumento x =como el valor local de x, pero cuando llama a la función, R encuentra x = 4en el entorno local desde el que está llamando.

Entonces, además de corregir el error cambiando el nombre del argumento o especificando explícitamente el entorno como se menciona en otras respuestas, también puede especificar eso x=xcuando llame a la función en lugar de cuando la defina. Para mí, especificar que x=xen la llamada fue la mejor solución, ya que no implica una sintaxis adicional o la acumulación de más y más nombres de variables.

rrr
fuente