Lorsque j'exécute une fonction dans R, j'exécute une autre fonction dans celle-ci. J'ai un code sur les lignes de ceci:
f_a <- function(b, c){
return(b + c)
}
f_e <- function(){
b = 2
c = 2
d = f_a(b, c)
print(d)
}
Cela fonctionne bien. Ce que je voudrais faire est ne pas passer les variables b, c
à la fonction f_a
. Je voudrais faire quelque chose comme ça (qui jette des erreurs)
f_a <- function(){
return(b + c)
}
f_e <- function(){
b = 2
c = 2
d = f_a()
print(d)
}
Est-il possible de faire cela en utilisant des environnements, des chemins de recherche ou tout autre moyen?
Je vous encourage à lire sur la portée lexicale , Mais je pense qu'une bonne approche pour éviter d'écrire beaucoup de variables pourrait être:
get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
potential <- names(formals(fun))
if ("..." %in% potential) {
if (missing(dots)) {
# return everything from parent frame
return(as.list(env))
}
else if (!is.list(dots)) {
stop("If provided, 'dots' should be a list.")
}
potential <- setdiff(potential, "...")
}
# get all formal arguments that can be found in parent frame
args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
# remove not found
args <- args[sapply(args, Negate(is.null))]
# return found args and dots
c(args, dots)
}
f_a <- function(b, c = 0, ..., d = 1) {
b <- b + 1
c(b = b, c = c, d = d, ...)
}
f_e <- function() {
b <- 2
c <- 2
arg_list <- get_args_for(f_a, dots = list(5))
do.call(f_a, arg_list)
}
> f_e()
b c d
3 2 1 5
Définir inherits = FALSE
par défaut garantit que nous n'obtenons que des variables de l'environnement spécifié . Nous pourrions également définir dots = NULL
lors de l'appel de get_args_for
pour ne pas transmettre toutes les variables, .__, mais laisser les ellipses vides.
Néanmoins, ce n'est pas tout à fait robuste, .__, car dots
est simplement ajouté à la fin, Et si certains arguments ne sont pas nommés, , Ils pourraient être mis en correspondance par la position .. les valeurs devraient être NULL
dans l'appel, il ne serait pas facile de le détecter.
Je vous déconseille fortement d'utiliser ces éléments dans un paquet R . Non seulement ce sera plutôt moche, vous obtiendrez de nombreuses notes de la vérification CMD de R. concernant des variables globales non définies.
Autres options.
f_a <- function() {
return(b + c)
}
f_e <- function() {
b <- 2
c <- 2
# replace f_a's enclosing environment with the current evaluation's environment
environment(f_a) <- environment()
d <- f_a()
d
}
> f_e()
[1] 4
Quelque chose comme ce qui précède ne fonctionnerait probablement pas dans un package R, .__, car je pense que les fonctions d’un package ont leurs environnements fermants verrouillés.
Ou:
f_a <- function() {
with(parent.frame(), {
b + c
})
}
f_e <- function() {
b <- 2
c <- 2
f_a()
}
> f_e()
[1] 4
Ainsi, vous ne modifiez pas de façon permanente l'environnement englobant de l'autre fonction . Cependant, les deux fonctions partageront un environnement, Un événement comme celui-ci pourrait se produire:
f_a <- function() {
with(parent.frame(), {
b <- b + 1
b + c
})
}
f_e <- function() {
b <- 2
c <- 2
d <- f_a()
c(b,d)
}
> f_e()
[1] 3 5
Où l'appel de la fonction interne modifie les valeurs de l'environnement externe.
Encore une autre option, un peu plus flexible, .__ puisqu'elle modifie temporairement l'environnement englobant en utilisant eval
. Cependant, certaines fonctions R détectent leur environnement d'exécution actuel par le biais de "magie daRk", et ne peut être dupé par eval
; voir cette discussion .
f_a <- function() {
b <- b + 1
b + c
}
f_e <- function() {
b <- 2
c <- 2
# use current environment as enclosing environment for f_a's evaluation
d <- eval(body(f_a), list(), enclos=environment())
c(b=b, d=d)
}
> f_e()
b d
2 5
Une option consiste à extraire explicitement a
et b
de l'environnement appelant:
f_a <- function(){
get('b', envir = parent.frame()) + get('c', envir = parent.frame())
}
f_e <- function(){
b = 2
c = 2
d = f_a()
d
}
f_e()
#> [1] 4
Vous pouvez également utiliser quote
pour retarder l'évaluation, puis eval
pour évaluer le code dans l'environnement appelant, en effectuant la même chose:
f_a <- function(){
eval(quote(b + c), parent.frame())
}
Cependant, ce n'est pas vraiment un moyen robuste d'écrire du code car il limite les moyens possibles pour appeler f_a
avec succès. Il est beaucoup plus facile de suivre un code qui transmet explicitement des variables.
Modifier:
La suggestion de @ alistaire d'utiliser quote
pour construire les expressions fait apparaître cette autre alternative qui semble encore moins laide:
expr_env <- new.env()
expr_env$f_a <- quote(b+c)
expr_env$f_z <- quote(x+y)
f_e<-function(){
b=2
c=2
d=eval( expr_env$f_a)
print(d)
}
Est-ce que définir la fonction en utilisant local
serait une alternative acceptable?
f_e<-function(){
b=2
c=2
d<-local({
b+c
})
print(d)
}
f_e()
[1] 4
Une alternative serait de ne renvoyer qu'un arbre d'analyse puis de terminer l'évaluation dans l'environnement "local" de la fonction. Cela me semble "moche":
expr_list<-function(){ f_a <- quote(b+c)
f_z <- quote(x+y)
list(f_a=f_a,f_z=f_z) }
f_e<-function(){
b=2
c=2
d=eval( (expr_list()$f_a))
print(d)
}
Vous pouvez affecter les variables à l'environnement global et utiliser la fonction inside.
f_a <- function(){
return(b + c)
}
f_e <- function(){
assign("b", 2, envir = .GlobalEnv)
assign("c", 2, envir = .GlobalEnv)
d = f_a()
print(d)
}
# > f_e()
# [1] 4