165 votes

Promesse déjà en cours d'évaluation : référence d'argument par défaut récursif ou problèmes antérieurs ?

Voici mon code R. Les fonctions sont définies comme suit :

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)
}

L'erreur en cours d'exécution est :

> test()
Error in test() :
promise already under evaluation: recursive default argument reference or earlier problems?

Si je substitue la définition de f dans celle de g, alors l'erreur disparaît.

Je me demandais quelle était l'erreur ? Comment la corriger si je ne substitue pas la définition de f dans celle de g ? Merci !


Mise à jour :

Merci ! Deux questions :

(1) si la fonction test prend en plus un argument pour f, ajouterez-vous quelque chose comme test <- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) } ? Dans les cas avec plus de récursions, est-ce une bonne et sûre pratique d'ajouter plus de . ?

(2) si f est un argument non-fonctionnel, par exemple g <- function(x, T, f=f){ exp(-f*x/T) } et test <- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }, utiliser le même nom pour les arguments formels et réels non fonctionnels est-il une bonne pratique sûre ou cela peut-il poser un problème potentiel ?

189voto

G. Grothendieck Points 40825

Les arguments formels de la forme x=x provoquent cela. En éliminant les deux cas où ils se produisent, nous obtenons ce qui suit. (La raison pour laquelle vous ne pouvez pas utiliser x=x dans les arguments formels d'une définition de fonction est qu'elle recherche d'abord l'argument par défaut à l'intérieur de la fonction elle-même, donc utiliser cette forme lui dit de s'utiliser comme défaut mais cela n'a pas été défini donc cela n'a aucun sens et nous obtenons une erreur.)

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

2 votes

Merci! Deux questions (1) si la fonction test prend un argument supplémentaire pour f, ajouterez-vous quelque chose comme test<- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) }? Dans les cas de plus de récursions, est-ce une bonne pratique d'ajouter plus de .? (2) si f est un argument non-fonction, par exemple _g <- function(x, T, f=f){ exp(-f_x/T) }* et test<- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }, utiliser le même nom pour les arguments formels et réels non fonctionnels est-il une bonne pratique et sûre ou cela pourrait-il causer des problèmes potentiels?

19 votes

Avez-vous d'autres solutions? Je passe quelques arguments assez profondément dans la chaîne de fonctions (environ 5 niveaux), et cette solution peut devenir ..... encombrante. :)

0 votes

Que se passe-t-il si je ne peux pas changer le nom d'un argument, par exemple, lorsque la fonction en question est une fonction système comme get?

23voto

xm1 Points 387

Si vous spécifiez le contexte d'évaluation des arguments, vous évitez le problème du même nom:

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

2 votes

Voici une meilleure façon, je pense que préciser l'environnement est plus clair

12voto

rrr Points 426

Comme déjà mentionné, le problème vient du fait d'avoir un argument de fonction défini comme lui-même. Cependant, je veux ajouter une explication de pourquoi c'est un problème car comprendre cela m'a conduit à une façon plus facile (pour moi) d'éviter le problème : spécifier simplement l'argument dans l'appel plutôt que dans la définition.

Ceci ne fonctionne pas :

x = 4
my.function <- function(x = x){} 
my.function() # erreur récursive!

mais cela fonctionne :

x = 4
my.function <- function(x){} 
my.function(x = x) # fonctionne bien!

Les arguments de fonction existent dans leur propre environnement local.

R cherche d'abord les variables dans l'environnement local, puis dans l'environnement global. C'est comme à l'intérieur d'une fonction une variable peut avoir le même nom qu'une variable dans l'environnement global, et R utilisera la définition locale.

Le fait que les définitions des arguments de fonction forment leur propre environnement local permet d'avoir des valeurs d'arguments par défaut basées sur d'autres valeurs d'arguments, comme

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

C'est pourquoi vous ne pouvez pas DÉFINIR une fonction comme my.function <- function(x = x){} mais vous pouvez APPELER la fonction en utilisant my.function(x = x). Lorsque vous définissez la fonction, R est confus car il trouve l'argument x = comme la valeur locale de x, mais lorsque vous appelez la fonction, R trouve x = 4 dans l'environnement local à partir duquel vous appelez.

Donc, en plus de corriger l'erreur en changeant le nom de l'argument ou en spécifiant explicitement l'environnement comme mentionné dans d'autres réponses, vous pouvez également simplement spécifier que x=x lorsque vous appelez la fonction au lieu de le faire lorsque vous la définissez. Pour moi, spécifier que x=x dans l'appel était la meilleure solution, car cela n'implique pas de syntaxe supplémentaire ou d'accumulation de noms de variables supplémentaires.

1voto

t4x0n Points 21

J'aime la réponse de G. Grothendieck, mais je me demandais s'il ne serait pas plus simple dans votre cas de ne pas inclure les noms de fonctions dans les paramètres des fonctions, comme ceci :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X